162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Marvell Xenon SDHC as a platform device 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Marvell, All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Hu Ziji <huziji@marvell.com> 862306a36Sopenharmony_ci * Date: 2016-8-24 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Inspired by Jisheng Zhang <jszhang@marvell.com> 1162306a36Sopenharmony_ci * Special thanks to Video BG4 project team. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/ktime.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/pm.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "sdhci-pltfm.h" 2362306a36Sopenharmony_ci#include "sdhci-xenon.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int xenon_enable_internal_clk(struct sdhci_host *host) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci u32 reg; 2862306a36Sopenharmony_ci ktime_t timeout; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL); 3162306a36Sopenharmony_ci reg |= SDHCI_CLOCK_INT_EN; 3262306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL); 3362306a36Sopenharmony_ci /* Wait max 20 ms */ 3462306a36Sopenharmony_ci timeout = ktime_add_ms(ktime_get(), 20); 3562306a36Sopenharmony_ci while (1) { 3662306a36Sopenharmony_ci bool timedout = ktime_after(ktime_get(), timeout); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 3962306a36Sopenharmony_ci if (reg & SDHCI_CLOCK_INT_STABLE) 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci if (timedout) { 4262306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n"); 4362306a36Sopenharmony_ci return -ETIMEDOUT; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci usleep_range(900, 1100); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Set SDCLK-off-while-idle */ 5262306a36Sopenharmony_cistatic void xenon_set_sdclk_off_idle(struct sdhci_host *host, 5362306a36Sopenharmony_ci unsigned char sdhc_id, bool enable) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u32 reg; 5662306a36Sopenharmony_ci u32 mask; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 5962306a36Sopenharmony_ci /* Get the bit shift basing on the SDHC index */ 6062306a36Sopenharmony_ci mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id)); 6162306a36Sopenharmony_ci if (enable) 6262306a36Sopenharmony_ci reg |= mask; 6362306a36Sopenharmony_ci else 6462306a36Sopenharmony_ci reg &= ~mask; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Enable/Disable the Auto Clock Gating function */ 7062306a36Sopenharmony_cistatic void xenon_set_acg(struct sdhci_host *host, bool enable) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u32 reg; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 7562306a36Sopenharmony_ci if (enable) 7662306a36Sopenharmony_ci reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci reg |= XENON_AUTO_CLKGATE_DISABLE_MASK; 7962306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Enable this SDHC */ 8362306a36Sopenharmony_cistatic void xenon_enable_sdhc(struct sdhci_host *host, 8462306a36Sopenharmony_ci unsigned char sdhc_id) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u32 reg; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 8962306a36Sopenharmony_ci reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 9062306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Force to clear BUS_TEST to 9562306a36Sopenharmony_ci * skip bus_test_pre and bus_test_post 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* Disable this SDHC */ 10162306a36Sopenharmony_cistatic void xenon_disable_sdhc(struct sdhci_host *host, 10262306a36Sopenharmony_ci unsigned char sdhc_id) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci u32 reg; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 10762306a36Sopenharmony_ci reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 10862306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Enable Parallel Transfer Mode */ 11262306a36Sopenharmony_cistatic void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host, 11362306a36Sopenharmony_ci unsigned char sdhc_id) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 reg; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 11862306a36Sopenharmony_ci reg |= BIT(sdhc_id); 11962306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Mask command conflict error */ 12362306a36Sopenharmony_cistatic void xenon_mask_cmd_conflict_err(struct sdhci_host *host) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 reg; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 12862306a36Sopenharmony_ci reg |= XENON_MASK_CMD_CONFLICT_ERR; 12962306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void xenon_retune_setup(struct sdhci_host *host) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 13562306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 13662306a36Sopenharmony_ci u32 reg; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Disable the Re-Tuning Request functionality */ 13962306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL); 14062306a36Sopenharmony_ci reg &= ~XENON_RETUNING_COMPATIBLE; 14162306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Disable the Re-tuning Interrupt */ 14462306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); 14562306a36Sopenharmony_ci reg &= ~SDHCI_INT_RETUNE; 14662306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE); 14762306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_INT_ENABLE); 14862306a36Sopenharmony_ci reg &= ~SDHCI_INT_RETUNE; 14962306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_INT_ENABLE); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Force to use Tuning Mode 1 */ 15262306a36Sopenharmony_ci host->tuning_mode = SDHCI_TUNING_MODE_1; 15362306a36Sopenharmony_ci /* Set re-tuning period */ 15462306a36Sopenharmony_ci host->tuning_count = 1 << (priv->tuning_count - 1); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * Operations inside struct sdhci_ops 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */ 16162306a36Sopenharmony_cistatic void xenon_reset_exit(struct sdhci_host *host, 16262306a36Sopenharmony_ci unsigned char sdhc_id, u8 mask) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci /* Only SOFTWARE RESET ALL will clear the register setting */ 16562306a36Sopenharmony_ci if (!(mask & SDHCI_RESET_ALL)) 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Disable tuning request and auto-retuning again */ 16962306a36Sopenharmony_ci xenon_retune_setup(host); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * The ACG should be turned off at the early init time, in order 17362306a36Sopenharmony_ci * to solve a possible issues with the 1.8V regulator stabilization. 17462306a36Sopenharmony_ci * The feature is enabled in later stage. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci xenon_set_acg(host, false); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci xenon_set_sdclk_off_idle(host, sdhc_id, false); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci xenon_mask_cmd_conflict_err(host); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void xenon_reset(struct sdhci_host *host, u8 mask) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 18662306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci sdhci_reset(host, mask); 18962306a36Sopenharmony_ci xenon_reset_exit(host, priv->sdhc_id, mask); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Xenon defines different values for HS200 and HS400 19462306a36Sopenharmony_ci * in Host_Control_2 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic void xenon_set_uhs_signaling(struct sdhci_host *host, 19762306a36Sopenharmony_ci unsigned int timing) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u16 ctrl_2; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 20262306a36Sopenharmony_ci /* Select Bus Speed Mode for host */ 20362306a36Sopenharmony_ci ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 20462306a36Sopenharmony_ci if (timing == MMC_TIMING_MMC_HS200) 20562306a36Sopenharmony_ci ctrl_2 |= XENON_CTRL_HS200; 20662306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR104) 20762306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 20862306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR12) 20962306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 21062306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR25) 21162306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 21262306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR50) 21362306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 21462306a36Sopenharmony_ci else if ((timing == MMC_TIMING_UHS_DDR50) || 21562306a36Sopenharmony_ci (timing == MMC_TIMING_MMC_DDR52)) 21662306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 21762306a36Sopenharmony_ci else if (timing == MMC_TIMING_MMC_HS400) 21862306a36Sopenharmony_ci ctrl_2 |= XENON_CTRL_HS400; 21962306a36Sopenharmony_ci sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void xenon_set_power(struct sdhci_host *host, unsigned char mode, 22362306a36Sopenharmony_ci unsigned short vdd) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 22662306a36Sopenharmony_ci u8 pwr = host->pwr; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci sdhci_set_power_noreg(host, mode, vdd); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (host->pwr == pwr) 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (host->pwr == 0) 23462306a36Sopenharmony_ci vdd = 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (!IS_ERR(mmc->supply.vmmc)) 23762306a36Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void xenon_voltage_switch(struct sdhci_host *host) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci /* Wait for 5ms after set 1.8V signal enable bit */ 24362306a36Sopenharmony_ci usleep_range(5000, 5500); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic unsigned int xenon_get_max_clock(struct sdhci_host *host) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (pltfm_host->clk) 25162306a36Sopenharmony_ci return sdhci_pltfm_clk_get_max_clock(host); 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci return pltfm_host->clock; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_xenon_ops = { 25762306a36Sopenharmony_ci .voltage_switch = xenon_voltage_switch, 25862306a36Sopenharmony_ci .set_clock = sdhci_set_clock, 25962306a36Sopenharmony_ci .set_power = xenon_set_power, 26062306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 26162306a36Sopenharmony_ci .reset = xenon_reset, 26262306a36Sopenharmony_ci .set_uhs_signaling = xenon_set_uhs_signaling, 26362306a36Sopenharmony_ci .get_max_clock = xenon_get_max_clock, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_xenon_pdata = { 26762306a36Sopenharmony_ci .ops = &sdhci_xenon_ops, 26862306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 26962306a36Sopenharmony_ci SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 27062306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* 27462306a36Sopenharmony_ci * Xenon Specific Operations in mmc_host_ops 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 27962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 28062306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 28162306a36Sopenharmony_ci u32 reg; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * HS400/HS200/eMMC HS doesn't have Preset Value register. 28562306a36Sopenharmony_ci * However, sdhci_set_ios will read HS400/HS200 Preset register. 28662306a36Sopenharmony_ci * Disable Preset Value register for HS400/HS200. 28762306a36Sopenharmony_ci * eMMC HS with preset_enabled set will trigger a bug in 28862306a36Sopenharmony_ci * get_preset_value(). 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if ((ios->timing == MMC_TIMING_MMC_HS400) || 29162306a36Sopenharmony_ci (ios->timing == MMC_TIMING_MMC_HS200) || 29262306a36Sopenharmony_ci (ios->timing == MMC_TIMING_MMC_HS)) { 29362306a36Sopenharmony_ci host->preset_enabled = false; 29462306a36Sopenharmony_ci host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 29562306a36Sopenharmony_ci host->flags &= ~SDHCI_PV_ENABLED; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); 29862306a36Sopenharmony_ci reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; 29962306a36Sopenharmony_ci sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sdhci_set_ios(mmc, ios); 30562306a36Sopenharmony_ci xenon_phy_adj(host, ios); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (host->clock > XENON_DEFAULT_SDCLK_FREQ) 30862306a36Sopenharmony_ci xenon_set_sdclk_off_idle(host, priv->sdhc_id, true); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int xenon_start_signal_voltage_switch(struct mmc_host *mmc, 31262306a36Sopenharmony_ci struct mmc_ios *ios) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * Before SD/SDIO set signal voltage, SD bus clock should be 31862306a36Sopenharmony_ci * disabled. However, sdhci_set_clock will also disable the Internal 31962306a36Sopenharmony_ci * clock in mmc_set_signal_voltage(). 32062306a36Sopenharmony_ci * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated. 32162306a36Sopenharmony_ci * Thus here manually enable internal clock. 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * After switch completes, it is unnecessary to disable internal clock, 32462306a36Sopenharmony_ci * since keeping internal clock active obeys SD spec. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci xenon_enable_internal_clk(host); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci xenon_soc_pad_ctrl(host, ios->signal_voltage); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable. 33262306a36Sopenharmony_ci * Thus SDHCI_CTRL_VDD_180 bit might not work then. 33362306a36Sopenharmony_ci * Skip the standard voltage switch to avoid any issue. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return sdhci_start_signal_voltage_switch(mmc, ios); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * Update card type. 34362306a36Sopenharmony_ci * priv->init_card_type will be used in PHY timing adjustment. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 34862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 34962306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Update card type*/ 35262306a36Sopenharmony_ci priv->init_card_type = card->type; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (host->timing == MMC_TIMING_UHS_DDR50 || 36062306a36Sopenharmony_ci host->timing == MMC_TIMING_MMC_DDR52) 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * Currently force Xenon driver back to support mode 1 only, 36562306a36Sopenharmony_ci * even though Xenon might claim to support mode 2 or mode 3. 36662306a36Sopenharmony_ci * It requires more time to test mode 2/mode 3 on more platforms. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci if (host->tuning_mode != SDHCI_TUNING_MODE_1) 36962306a36Sopenharmony_ci xenon_retune_setup(host); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return sdhci_execute_tuning(mmc, opcode); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 37762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 37862306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 37962306a36Sopenharmony_ci u32 reg; 38062306a36Sopenharmony_ci u8 sdhc_id = priv->sdhc_id; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci sdhci_enable_sdio_irq(mmc, enable); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (enable) { 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Set SDIO Card Inserted indication 38762306a36Sopenharmony_ci * to enable detecting SDIO async irq. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 39062306a36Sopenharmony_ci reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 39162306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci /* Clear SDIO Card Inserted indication */ 39462306a36Sopenharmony_ci reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 39562306a36Sopenharmony_ci reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 39662306a36Sopenharmony_ci sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void xenon_replace_mmc_host_ops(struct sdhci_host *host) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci host->mmc_host_ops.set_ios = xenon_set_ios; 40362306a36Sopenharmony_ci host->mmc_host_ops.start_signal_voltage_switch = 40462306a36Sopenharmony_ci xenon_start_signal_voltage_switch; 40562306a36Sopenharmony_ci host->mmc_host_ops.init_card = xenon_init_card; 40662306a36Sopenharmony_ci host->mmc_host_ops.execute_tuning = xenon_execute_tuning; 40762306a36Sopenharmony_ci host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * Parse Xenon specific DT properties: 41262306a36Sopenharmony_ci * sdhc-id: the index of current SDHC. 41362306a36Sopenharmony_ci * Refer to XENON_SYS_CFG_INFO register 41462306a36Sopenharmony_ci * tun-count: the interval between re-tuning 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_cistatic int xenon_probe_params(struct platform_device *pdev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 41962306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 42062306a36Sopenharmony_ci struct mmc_host *mmc = host->mmc; 42162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 42262306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 42362306a36Sopenharmony_ci u32 sdhc_id, nr_sdhc; 42462306a36Sopenharmony_ci u32 tuning_count; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Disable HS200 on Armada AP806 */ 42762306a36Sopenharmony_ci if (priv->hw_version == XENON_AP806) 42862306a36Sopenharmony_ci host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci sdhc_id = 0x0; 43162306a36Sopenharmony_ci if (!device_property_read_u32(dev, "marvell,xenon-sdhc-id", &sdhc_id)) { 43262306a36Sopenharmony_ci nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO); 43362306a36Sopenharmony_ci nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK; 43462306a36Sopenharmony_ci if (unlikely(sdhc_id > nr_sdhc)) { 43562306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n", 43662306a36Sopenharmony_ci sdhc_id, nr_sdhc); 43762306a36Sopenharmony_ci return -EINVAL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci priv->sdhc_id = sdhc_id; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci tuning_count = XENON_DEF_TUNING_COUNT; 44362306a36Sopenharmony_ci if (!device_property_read_u32(dev, "marvell,xenon-tun-count", 44462306a36Sopenharmony_ci &tuning_count)) { 44562306a36Sopenharmony_ci if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) { 44662306a36Sopenharmony_ci dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n", 44762306a36Sopenharmony_ci XENON_DEF_TUNING_COUNT); 44862306a36Sopenharmony_ci tuning_count = XENON_DEF_TUNING_COUNT; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci priv->tuning_count = tuning_count; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return xenon_phy_parse_params(dev, host); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int xenon_sdhc_prepare(struct sdhci_host *host) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 45962306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 46062306a36Sopenharmony_ci u8 sdhc_id = priv->sdhc_id; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Enable SDHC */ 46362306a36Sopenharmony_ci xenon_enable_sdhc(host, sdhc_id); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Enable ACG */ 46662306a36Sopenharmony_ci xenon_set_acg(host, true); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Enable Parallel Transfer Mode */ 46962306a36Sopenharmony_ci xenon_enable_sdhc_parallel_tran(host, sdhc_id); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Disable SDCLK-Off-While-Idle before card init */ 47262306a36Sopenharmony_ci xenon_set_sdclk_off_idle(host, sdhc_id, false); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci xenon_mask_cmd_conflict_err(host); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void xenon_sdhc_unprepare(struct sdhci_host *host) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 48262306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 48362306a36Sopenharmony_ci u8 sdhc_id = priv->sdhc_id; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* disable SDHC */ 48662306a36Sopenharmony_ci xenon_disable_sdhc(host, sdhc_id); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int xenon_probe(struct platform_device *pdev) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 49262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 49362306a36Sopenharmony_ci struct sdhci_host *host; 49462306a36Sopenharmony_ci struct xenon_priv *priv; 49562306a36Sopenharmony_ci int err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata, 49862306a36Sopenharmony_ci sizeof(struct xenon_priv)); 49962306a36Sopenharmony_ci if (IS_ERR(host)) 50062306a36Sopenharmony_ci return PTR_ERR(host); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 50362306a36Sopenharmony_ci priv = sdhci_pltfm_priv(pltfm_host); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci priv->hw_version = (unsigned long)device_get_match_data(&pdev->dev); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * Link Xenon specific mmc_host_ops function, 50962306a36Sopenharmony_ci * to replace standard ones in sdhci_ops. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci xenon_replace_mmc_host_ops(host); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (dev->of_node) { 51462306a36Sopenharmony_ci pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 51562306a36Sopenharmony_ci if (IS_ERR(pltfm_host->clk)) { 51662306a36Sopenharmony_ci err = PTR_ERR(pltfm_host->clk); 51762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); 51862306a36Sopenharmony_ci goto free_pltfm; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci err = clk_prepare_enable(pltfm_host->clk); 52162306a36Sopenharmony_ci if (err) 52262306a36Sopenharmony_ci goto free_pltfm; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci priv->axi_clk = devm_clk_get(&pdev->dev, "axi"); 52562306a36Sopenharmony_ci if (IS_ERR(priv->axi_clk)) { 52662306a36Sopenharmony_ci err = PTR_ERR(priv->axi_clk); 52762306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 52862306a36Sopenharmony_ci goto err_clk; 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci err = clk_prepare_enable(priv->axi_clk); 53162306a36Sopenharmony_ci if (err) 53262306a36Sopenharmony_ci goto err_clk; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci err = mmc_of_parse(host->mmc); 53762306a36Sopenharmony_ci if (err) 53862306a36Sopenharmony_ci goto err_clk_axi; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci sdhci_get_property(pdev); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci xenon_set_acg(host, false); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Xenon specific parameters parse */ 54562306a36Sopenharmony_ci err = xenon_probe_params(pdev); 54662306a36Sopenharmony_ci if (err) 54762306a36Sopenharmony_ci goto err_clk_axi; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci err = xenon_sdhc_prepare(host); 55062306a36Sopenharmony_ci if (err) 55162306a36Sopenharmony_ci goto err_clk_axi; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 55462306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 55562306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 55662306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 55762306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 55862306a36Sopenharmony_ci pm_suspend_ignore_children(&pdev->dev, 1); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci err = sdhci_add_host(host); 56162306a36Sopenharmony_ci if (err) 56262306a36Sopenharmony_ci goto remove_sdhc; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciremove_sdhc: 56962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 57062306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 57162306a36Sopenharmony_ci xenon_sdhc_unprepare(host); 57262306a36Sopenharmony_cierr_clk_axi: 57362306a36Sopenharmony_ci clk_disable_unprepare(priv->axi_clk); 57462306a36Sopenharmony_cierr_clk: 57562306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 57662306a36Sopenharmony_cifree_pltfm: 57762306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 57862306a36Sopenharmony_ci return err; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic void xenon_remove(struct platform_device *pdev) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 58462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 58562306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 58862306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 58962306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci sdhci_remove_host(host, 0); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci xenon_sdhc_unprepare(host); 59462306a36Sopenharmony_ci clk_disable_unprepare(priv->axi_clk); 59562306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 60162306a36Sopenharmony_cistatic int xenon_suspend(struct device *dev) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 60462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 60562306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 60662306a36Sopenharmony_ci int ret; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci priv->restore_needed = true; 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci#endif 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#ifdef CONFIG_PM 61662306a36Sopenharmony_cistatic int xenon_runtime_suspend(struct device *dev) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 61962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 62062306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = sdhci_runtime_suspend_host(host); 62462306a36Sopenharmony_ci if (ret) 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (host->tuning_mode != SDHCI_TUNING_MODE_3) 62862306a36Sopenharmony_ci mmc_retune_needed(host->mmc); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * Need to update the priv->clock here, or when runtime resume 63362306a36Sopenharmony_ci * back, phy don't aware the clock change and won't adjust phy 63462306a36Sopenharmony_ci * which will cause cmd err 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci priv->clock = 0; 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int xenon_runtime_resume(struct device *dev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 64362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 64462306a36Sopenharmony_ci struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 64562306a36Sopenharmony_ci int ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = clk_prepare_enable(pltfm_host->clk); 64862306a36Sopenharmony_ci if (ret) { 64962306a36Sopenharmony_ci dev_err(dev, "can't enable mainck\n"); 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (priv->restore_needed) { 65462306a36Sopenharmony_ci ret = xenon_sdhc_prepare(host); 65562306a36Sopenharmony_ci if (ret) 65662306a36Sopenharmony_ci goto out; 65762306a36Sopenharmony_ci priv->restore_needed = false; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci ret = sdhci_runtime_resume_host(host, 0); 66162306a36Sopenharmony_ci if (ret) 66262306a36Sopenharmony_ci goto out; 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ciout: 66562306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci#endif /* CONFIG_PM */ 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_xenon_dev_pm_ops = { 67162306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend, 67262306a36Sopenharmony_ci pm_runtime_force_resume) 67362306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(xenon_runtime_suspend, 67462306a36Sopenharmony_ci xenon_runtime_resume, 67562306a36Sopenharmony_ci NULL) 67662306a36Sopenharmony_ci}; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic const struct of_device_id sdhci_xenon_dt_ids[] = { 67962306a36Sopenharmony_ci { .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806}, 68062306a36Sopenharmony_ci { .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807}, 68162306a36Sopenharmony_ci { .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110}, 68262306a36Sopenharmony_ci { .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700}, 68362306a36Sopenharmony_ci {} 68462306a36Sopenharmony_ci}; 68562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci#ifdef CONFIG_ACPI 68862306a36Sopenharmony_cistatic const struct acpi_device_id sdhci_xenon_acpi_ids[] = { 68962306a36Sopenharmony_ci { .id = "MRVL0002", XENON_AP806}, 69062306a36Sopenharmony_ci { .id = "MRVL0003", XENON_AP807}, 69162306a36Sopenharmony_ci { .id = "MRVL0004", XENON_CP110}, 69262306a36Sopenharmony_ci {} 69362306a36Sopenharmony_ci}; 69462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, sdhci_xenon_acpi_ids); 69562306a36Sopenharmony_ci#endif 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic struct platform_driver sdhci_xenon_driver = { 69862306a36Sopenharmony_ci .driver = { 69962306a36Sopenharmony_ci .name = "xenon-sdhci", 70062306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 70162306a36Sopenharmony_ci .of_match_table = sdhci_xenon_dt_ids, 70262306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(sdhci_xenon_acpi_ids), 70362306a36Sopenharmony_ci .pm = &sdhci_xenon_dev_pm_ops, 70462306a36Sopenharmony_ci }, 70562306a36Sopenharmony_ci .probe = xenon_probe, 70662306a36Sopenharmony_ci .remove_new = xenon_remove, 70762306a36Sopenharmony_ci}; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cimodule_platform_driver(sdhci_xenon_driver); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC"); 71262306a36Sopenharmony_ciMODULE_AUTHOR("Hu Ziji <huziji@marvell.com>"); 71362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 714