/* * Copyright (C) 2010 Google, Inc. * Copyright (c) 2012-2018, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdhci-pltfm.h" /* Tegra SDHOST controller vendor register definitions */ #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 #define SDHCI_CLOCK_CTRL_TAP_MASK 0xFF #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 #define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24 #define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1F #define SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE BIT(6) #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) #define SDHCI_CLOCK_CTRL_SDMMC_CLK BIT(0) #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL 0x104 #define SDHCI_SYS_SW_CTRL_STROBE_EN 0x80000000 #define SDHCI_TEGRA_VENDOR_ERR_INTR_STATUS 0x108 #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES 0x10C #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 #define SDHCI_MISC_CTRL_SDMMC_SPARE_0_MASK 0xFFFE #define SDHCI_TEGRA_VENDOR_MISC_CTRL_1 0x124 #define SDHCI_TEGRA_VENDOR_MISC_CTRL_2 0x128 #define SDHCI_MISC_CTRL_2_CLK_OVR_ON 0x40000000 #define SDMMC_VNDR_IO_TRIM_CTRL_0 0x1AC #define SDMMC_VNDR_IO_TRIM_CTRL_0_SEL_VREG_MASK 0x4 #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG 0x1B0 #define SDHCI_DLLCAL_CFG_EN_CALIBRATE 0x80000000 #define SDHCI_DLLCAL_CFG_STATUS 0x1BC #define SDHCI_DLLCAL_CFG_STATUS_DLL_ACTIVE 0x80000000 #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 #define SDHCI_TUN_CTRL0_TUNING_ITER_MASK 0x7 #define SDHCI_TUN_CTRL0_TUNING_ITER_SHIFT 13 #define SDHCI_TUN_CTRL0_TUNING_WORD_SEL_MASK 0x7 #define SDHCI_VNDR_TUN_CTRL0_0_TUN_ITER_MASK 0x000E000 #define TUNING_WORD_SEL_MASK 0x7 #define SDHCI_TEGRA_VNDR_TUNING_STATUS0 0x1C8 #define SDHCI_TEGRA_VNDR_TUNING_STATUS1 0x1CC #define SDHCI_TEGRA_VNDR_TUNING_STATUS1_TAP_MASK 0xFF #define SDHCI_TEGRA_VNDR_TUNING_STATUS1_END_TAP_SHIFT 8 #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1E0 #define SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD_MASK 0x80000000 #define SDHCI_TEGRA_SDMEMCOMP_PADCTRL_VREF_SEL 0x0000000F #define SDHCI_TEGRA_SDMEMCOMP_PADCTRL_DRVUP_OVR 0x07F00000 #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 #define SDHCI_AUTO_CAL_START BIT(31) #define SDHCI_AUTO_CAL_ENABLE BIT(29) #define SDHCI_AUTO_CAL_PUPD_OFFSETS 0x00007F7F #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1EC #define SDHCI_TEGRA_AUTO_CAL_ACTIVE 0x80000000 #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) #define NVQUIRK_ENABLE_SDR50 BIT(3) #define NVQUIRK_ENABLE_SDR104 BIT(4) #define NVQUIRK_ENABLE_DDR50 BIT(5) #define NVQUIRK_HAS_PADCALIB BIT(6) #define NVQUIRK_HW_TAP_CONFIG BIT(7) #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) #define NVQUIRK_USE_PLATFORM_TUNING BIT(9) #define NVQUIRK_READ_REG_AFTER_WRITE BIT(10) #define NVQUIRK_SHADOW_XFER_MODE_WRITE BIT(11) /* Quirk to identify SLCG registers */ #define NVQUIRK_SDMMC_CLK_OVERRIDE BIT(12) #define NVQUIRK_UPDATE_PIN_CNTRL_REG BIT(13) #define MAX_CLK_PARENTS 5 #define MAX_DIVISOR_VALUE 128 #define MAX_TAP_VALUE 256 #define SET_REQ_TAP 0x0 #define SET_DDR_TAP 0x1 #define SET_DEFAULT_TAP 0x2 #define TUNING_WORD_BIT_SIZE 32 #define SDHOST_LOW_VOLT_MIN 1800000 /* Set min identification clock of 400 KHz */ #define SDMMC_TEGRA_FALLBACK_CLK_HZ 400000 #define SDHCI_RTPM_MSEC_TMOUT 10 #define SDMMC_EMC_MAX_FREQ 150000000 #define SDMMC_TIMEOUT_CLK_FREQ_HZ 12000000 #define SDMMC_TIMEOUT_CLK_FREQ_MHZ 12 static unsigned int sdmmc_emc_clinet_id[] = { TEGRA_BWMGR_CLIENT_SDMMC1, TEGRA_BWMGR_CLIENT_SDMMC2, TEGRA_BWMGR_CLIENT_SDMMC3, TEGRA_BWMGR_CLIENT_SDMMC4 }; /* uhs mask can be used to mask any of the UHS modes support */ #define MMC_UHS_MASK_SDR12 0x1 #define MMC_UHS_MASK_SDR25 0x2 #define MMC_UHS_MASK_SDR50 0x4 #define MMC_UHS_MASK_DDR50 0x8 #define MMC_UHS_MASK_SDR104 0x10 #define MMC_MASK_HS200 0x20 #define MMC_MASK_HS400 0x40 #define MMC_MASK_SD_HS 0x80 static char prod_device_states[MMC_TIMING_COUNTER][20] = { "prod_c_ds", /* MMC_TIMING_LEGACY */ "prod_c_hs", /* MMC_TIMING_MMC_HS */ "prod_c_hs", /* MMC_TIMING_SD_HS */ "prod_c_sdr12", /* MMC_TIMING_UHS_SDR12 */ "prod_c_sdr25", /* MMC_TIMING_UHS_SDR25 */ "prod_c_sdr50", /* MMC_TIMING_UHS_SDR50 */ "prod_c_sdr104", /* MMC_TIMING_UHS_SDR104 */ "prod_c_ddr52", /* MMC_TIMING_UHS_DDR50 */ "prod_c_ddr52", /* MMC_TIMING_MMC_DDR52 */ "prod_c_hs200", /* MMC_TIMING_MMC_HS200 */ "prod_c_hs400", /* MMC_TIMING_MMC_HS400 */ }; struct sdhci_tegra_soc_data { const struct sdhci_pltfm_data *pdata; u32 nvquirks; u32 cqequirks; }; struct sdhci_tegra_clk_src_data { struct clk *parent_clk[MAX_CLK_PARENTS]; const char *parent_clk_name[MAX_CLK_PARENTS]; unsigned long parent_clk_rate[MAX_CLK_PARENTS]; u8 parent_clk_src_cnt; int curr_parent_clk_idx; }; struct sdhci_tegra { const struct sdhci_tegra_soc_data *soc_data; struct gpio_desc *power_gpio; struct reset_control *rst; bool ddr_signaling; bool pad_calib_required; struct clk *tmclk; struct sdhci_tegra_clk_src_data *clk_src_data; struct sdhci_host *host; struct delayed_work detect_delay; unsigned long curr_clk_rate; unsigned long max_clk_limit; unsigned long max_ddr_clk_limit; u32 boot_detect_delay; struct tegra_prod *prods; u8 tuned_tap_delay; bool rate_change_needs_clk; struct tegra_bwmgr_client *emc_clk; unsigned int tuning_status; #define TUNING_STATUS_DONE 1 #define TUNING_STATUS_RETUNE 2 bool disable_auto_cal; int timing; bool set_1v8_calib_offsets; int current_voltage; unsigned int cd_irq; bool pwrdet_support; bool update_pinctrl_settings; bool wake_enable_failed; bool cd_wakeup_capable; int cd_gpio; bool enable_hwcq; struct pinctrl *pinctrl_sdmmc; struct pinctrl_state *e_33v_enable; struct pinctrl_state *e_33v_disable; struct pinctrl_state *schmitt_enable[2]; struct pinctrl_state *schmitt_disable[2]; struct pinctrl_state *drv_code_strength; struct pinctrl_state *default_drv_code_strength; bool en_periodic_cflush; /* Enable periodic cache flush for eMMC */ u8 uhs_mask; unsigned int instance; int volt_switch_gpio; bool force_non_rem_rescan; bool static_parent_clk_mapping; int parent_clk_index[MMC_TIMING_COUNTER]; bool disable_rtpm; bool disable_clk_gate; bool is_rail_enabled; bool vqmmc_always_on; bool vmmc_always_on; bool slcg_status; bool en_periodic_calib; unsigned int min_tap_delay; unsigned int max_tap_delay; ktime_t timestamp; }; static int sdhci_tegra_parse_parent_list_from_dt(struct platform_device *pdev, struct sdhci_tegra *host); /* Module params */ static unsigned int en_boot_part_access; static void sdhci_tegra_debugfs_init(struct sdhci_host *host); static void tegra_sdhci_vendor_trim_clear_sel_vreg(struct sdhci_host *host, bool enable); static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap, int type); static int tegra_sdhci_suspend(struct sdhci_host *host); static int tegra_sdhci_resume(struct sdhci_host *host); static void tegra_sdhci_complete(struct sdhci_host *host); static int tegra_sdhci_runtime_suspend(struct sdhci_host *host); static int tegra_sdhci_runtime_resume(struct sdhci_host *host); static void tegra_sdhci_post_resume(struct sdhci_host *host); static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock); static void tegra_sdhci_update_sdmmc_pinctrl_register(struct sdhci_host *sdhci, bool set); static bool tegra_sdhci_is_clk_enabled(struct sdhci_host *host, int reg) { if (host->mmc->is_host_clk_enabled) { return true; } else { dev_err(mmc_dev(host->mmc), "Reg 0x%x being accessed without clock\n", reg); WARN_ON(1); return false; } } static u8 tegra_sdhci_readb(struct sdhci_host *host, int reg) { if (!tegra_sdhci_is_clk_enabled(host, reg)) return 0; return readb(host->ioaddr + reg); } static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; if (!tegra_sdhci_is_clk_enabled(host, reg)) return 0; if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && (reg == SDHCI_HOST_VERSION))) { /* Erratum: Version register is invalid in HW. */ return SDHCI_SPEC_200; } return readw(host->ioaddr + reg); } static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) { if (!tegra_sdhci_is_clk_enabled(host, reg)) return 0; return readl(host->ioaddr + reg); } static void tegra_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; if (!tegra_sdhci_is_clk_enabled(host, reg)) return; writeb(val, host->ioaddr + reg); if (soc_data->nvquirks & NVQUIRK_READ_REG_AFTER_WRITE) readb(host->ioaddr + reg); } static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; if (!tegra_sdhci_is_clk_enabled(host, reg)) return; if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_WRITE) { switch (reg) { case SDHCI_TRANSFER_MODE: /* * Postpone this write, we must do it together with a * command write that is down below. */ pltfm_host->xfer_mode_shadow = val; return; case SDHCI_COMMAND: writel((val << 16) | pltfm_host->xfer_mode_shadow, host->ioaddr + SDHCI_TRANSFER_MODE); if (soc_data->nvquirks & NVQUIRK_READ_REG_AFTER_WRITE) readl(host->ioaddr + SDHCI_TRANSFER_MODE); return; } } writew(val, host->ioaddr + reg); if (soc_data->nvquirks & NVQUIRK_READ_REG_AFTER_WRITE) readw(host->ioaddr + reg); } static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; if (!tegra_sdhci_is_clk_enabled(host, reg)) return; /* Seems like we're getting spurious timeout and crc errors, so * disable signalling of them. In case of real errors software * timers should take care of eventually detecting them. */ if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); writel(val, host->ioaddr + reg); if (soc_data->nvquirks & NVQUIRK_READ_REG_AFTER_WRITE) readl(host->ioaddr + reg); if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && (reg == SDHCI_INT_ENABLE))) { /* Erratum: Must enable block gap interrupt detection */ u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); if (val & SDHCI_INT_CARD_INT) gap_ctrl |= 0x8; else gap_ctrl &= ~0x8; writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); if (soc_data->nvquirks & NVQUIRK_READ_REG_AFTER_WRITE) readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); } } static void tegra_sdhci_dump_vendor_regs(struct sdhci_host *host) { int reg, tuning_status; u32 tap_delay; u32 trim_delay; u8 i; pr_debug("======= %s: Tuning windows =======\n", mmc_hostname(host->mmc)); reg = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); for (i = 0; i <= TUNING_WORD_SEL_MASK; i++) { reg &= ~SDHCI_TUN_CTRL0_TUNING_WORD_SEL_MASK; reg |= i; sdhci_writel(host, reg, SDHCI_VNDR_TUN_CTRL0_0); tuning_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUNING_STATUS0); pr_debug("%s: tuning window[%d]: %#x\n", mmc_hostname(host->mmc), i, tuning_status); } reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); tap_delay = reg >> SDHCI_CLOCK_CTRL_TAP_SHIFT; tap_delay &= SDHCI_CLOCK_CTRL_TAP_MASK; trim_delay = reg >> SDHCI_CLOCK_CTRL_TRIM_SHIFT; trim_delay &= SDHCI_CLOCK_CTRL_TRIM_MASK; pr_debug("sdhci: Tap value: %u | Trim value: %u\n", tap_delay, trim_delay); pr_debug("==================================\n"); pr_debug("Vendor clock ctrl: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL)); pr_debug("Vendor SysSW ctrl: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL)); pr_debug("Vendor Err interrupt status : %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_ERR_INTR_STATUS)); pr_debug("Vendor Cap overrides: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES)); pr_debug("Vendor Misc ctrl: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL)); pr_debug("Vendor Misc ctrl_1: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_1)); pr_debug("Vendor Misc ctrl_2: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2)); pr_debug("Vendor IO trim ctrl: %#x\n", sdhci_readl(host, SDMMC_VNDR_IO_TRIM_CTRL_0)); pr_debug("Vendor Tuning ctrl: %#x\n", sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0)); pr_debug("SDMEM comp padctrl: %#x\n", sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL)); pr_debug("Autocal config: %#x\n", sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG)); pr_debug("Autocal status: %#x\n", sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_STATUS)); pr_debug("Tuning Status1: %#x\n", sdhci_readl(host, SDHCI_TEGRA_VNDR_TUNING_STATUS1)); } static void tegra_sdhci_card_event(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int err = 0; tegra_host->tuning_status = TUNING_STATUS_RETUNE; host->is_calib_done = false; if (!host->mmc->rem_card_present) { tegra_host->set_1v8_calib_offsets = false; if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc) && regulator_is_enabled(host->mmc->supply.vmmc) && !tegra_host->vmmc_always_on) { regulator_disable(host->mmc->supply.vmmc); pr_info("%s: Disabling vmmc regulator\n", mmc_hostname(host->mmc)); } } else { if (!IS_ERR_OR_NULL(host->mmc->supply.vmmc) && !regulator_is_enabled(host->mmc->supply.vmmc) && !tegra_host->vmmc_always_on) { err = regulator_enable(host->mmc->supply.vmmc); pr_info("%s: Enabling vmmc regulator\n", mmc_hostname(host->mmc)); if (err) { pr_warn("%s: Failed to enable vmmc regulator: %d\n", mmc_hostname(host->mmc), err); host->mmc->supply.vmmc = ERR_PTR(-EINVAL); } } } } static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) { return mmc_gpio_get_ro(host->mmc); } static void tegra_sdhci_post_init(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; int reg, timeout = 5; if ((mmc->ios.timing == MMC_TIMING_MMC_DDR52) || (mmc->ios.timing == MMC_TIMING_UHS_DDR50)) { /* * Tegra SDMMC controllers support DDR mode with only clock * divisor 1. Set the clock frequency here again to ensure * host and device clocks are correctly configured. */ tegra_sdhci_set_clock(host, host->max_clk); } else if (mmc->ios.timing == MMC_TIMING_MMC_HS400) { reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); reg |= SDHCI_DLLCAL_CFG_EN_CALIBRATE; sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); mdelay(1); /* Wait until DLL calibration is done */ do { if (!(sdhci_readl(host, SDHCI_DLLCAL_CFG_STATUS) & SDHCI_DLLCAL_CFG_STATUS_DLL_ACTIVE)) break; mdelay(1); timeout--; } while (timeout); if (!timeout) dev_err(mmc_dev(host->mmc), "DLL calibration timed out\n"); } } static void tegra_sdhci_hs400_enhanced_strobe(struct sdhci_host *host, bool enable) { int reg; reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); if (enable) reg |= SDHCI_SYS_SW_CTRL_STROBE_EN; else reg &= ~SDHCI_SYS_SW_CTRL_STROBE_EN; sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); tegra_sdhci_set_tap(host, 0, SET_DEFAULT_TAP); } static int tegra_sdhci_get_max_tuning_loop_counter(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int err = 0; err = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[host->mmc->ios.timing], tegra_host->prods, 0, SDHCI_VNDR_TUN_CTRL0_0, SDHCI_VNDR_TUN_CTRL0_0_TUN_ITER_MASK); if (err) dev_err(mmc_dev(host->mmc), "%s: error %d in tuning iteration update\n", __func__, err); return 257; } static bool tegra_sdhci_skip_retuning(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); if (tegra_host->tuning_status == TUNING_STATUS_DONE) { dev_info(mmc_dev(host->mmc), "Tuning done, restoring the best tap value : %u\n", tegra_host->tuned_tap_delay); tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay, SET_REQ_TAP); return true; } return false; } static void tegra_sdhci_apply_tuning_correction(struct sdhci_host *host, u16 tun_iter, u8 upthres, u8 lowthres, u8 fixed_tap) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); u32 reg, mask = 0x00000001; u8 i, j, edge1; unsigned int tun_word; bool start_fail_def = false; bool start_pass_def = false; bool end_fail_def = false; bool end_pass_def = false; bool first_pass_def = false; bool first_fail_def = false; u8 start_fail = 0; u8 end_fail = 0; u8 start_pass = 0; u8 end_pass = 0; u8 first_fail = 0; u8 first_pass = 0; /*Select the first valid window with starting and ending edges defined*/ for (i = 0; i <= TUNING_WORD_SEL_MASK; i++) { if (i == (tun_iter/TUNING_WORD_BIT_SIZE)) break; j = 0; reg = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); reg &= ~TUNING_WORD_SEL_MASK; reg |= i; sdhci_writel(host, reg, SDHCI_VNDR_TUN_CTRL0_0); tun_word = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUNING_STATUS0); while (j <= (TUNING_WORD_BIT_SIZE - 1)) { if (!(tun_word & (mask << j)) && !start_fail_def) { start_fail = i*TUNING_WORD_BIT_SIZE + j; start_fail_def = true; if (!first_fail_def) { first_fail = start_fail; first_fail_def = true; } } else if ((tun_word & (mask << j)) && !start_pass_def && start_fail_def) { start_pass = i*TUNING_WORD_BIT_SIZE + j; start_pass_def = true; if (!first_pass_def) { first_pass = start_pass; first_pass_def = true; } } else if (!(tun_word & (mask << j)) && start_fail_def && start_pass_def && !end_pass_def) { end_pass = i*TUNING_WORD_BIT_SIZE + j - 1; end_pass_def = true; } else if ((tun_word & (mask << j)) && start_pass_def && start_fail_def && end_pass_def) { end_fail = i*TUNING_WORD_BIT_SIZE + j - 1; end_fail_def = true; if ((end_pass - start_pass) >= upthres) { start_fail = end_pass + 1; start_pass = end_fail + 1; end_pass_def = false; end_fail_def = false; j++; continue; } else if ((end_pass - start_pass) < lowthres) { start_pass = end_fail + 1; end_pass_def = false; end_fail_def = false; j++; continue; } break; } j++; if ((i*TUNING_WORD_BIT_SIZE + j) == tun_iter - 1) break; } if (start_pass_def && end_pass_def && start_fail_def && end_fail_def) { tegra_host->tuned_tap_delay = start_pass + (end_pass - start_pass)/2; return; } } /* * If no edge found, retain tap set by HW tuning */ if (!first_fail_def) { WARN_ON("No edge detected\n"); reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); tegra_host->tuned_tap_delay = ((reg >> SDHCI_CLOCK_CTRL_TAP_SHIFT) & SDHCI_CLOCK_CTRL_TAP_MASK); } /* * Set tap based on fixed value relative to first edge * if no valid windows found */ if (!end_fail_def && first_fail_def && first_pass_def) { edge1 = first_fail + (first_pass - first_fail)/2; if ((edge1 - 1) > fixed_tap) tegra_host->tuned_tap_delay = edge1 - fixed_tap; else tegra_host->tuned_tap_delay = edge1 + fixed_tap; } } static void tegra_sdhci_post_tuning(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); u32 reg; u8 start_tap, end_tap, window_width, upperthreshold, lowerthreshold; u8 fixed_tap; u16 num_tun_iter; u32 clk_rate_mhz, period, bestcase, worstcase; u32 avg_tap_delay, min_tap_delay, max_tap_delay; min_tap_delay = tegra_host->min_tap_delay; max_tap_delay = tegra_host->max_tap_delay; if (!min_tap_delay || !max_tap_delay) { pr_info("%s: Tuning correction cannot be applied", mmc_hostname(host->mmc)); goto retain_hw_tun_tap; } clk_rate_mhz = tegra_host->curr_clk_rate/1000000; period = 1000000/clk_rate_mhz; bestcase = period/min_tap_delay; worstcase = period/max_tap_delay; avg_tap_delay = (period*2)/(min_tap_delay + max_tap_delay); upperthreshold = ((2*worstcase) + bestcase)/2; lowerthreshold = worstcase/4; fixed_tap = avg_tap_delay/2; reg = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUNING_STATUS1); start_tap = reg & SDHCI_TEGRA_VNDR_TUNING_STATUS1_TAP_MASK; end_tap = (reg >> SDHCI_TEGRA_VNDR_TUNING_STATUS1_END_TAP_SHIFT) & SDHCI_TEGRA_VNDR_TUNING_STATUS1_TAP_MASK; window_width = end_tap - start_tap; num_tun_iter = (sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0) & SDHCI_VNDR_TUN_CTRL0_0_TUN_ITER_MASK) >> SDHCI_TUN_CTRL0_TUNING_ITER_SHIFT; switch (num_tun_iter) { case 0: num_tun_iter = 40; break; case 1: num_tun_iter = 64; break; case 2: num_tun_iter = 128; break; case 3: num_tun_iter = 196; break; case 4: num_tun_iter = 256; break; default: WARN_ON("Invalid value of number of tuning iterations"); } /* * Apply tuning correction if partial window is selected by HW tuning * or window merge is detected */ if ((start_tap == 0) || (end_tap == 254) || ((end_tap == 126) && (num_tun_iter == 128)) || (end_tap == (num_tun_iter - 1)) || (window_width >= upperthreshold)) { tegra_sdhci_dump_vendor_regs(host); pr_info("%s: Applying tuning correction\n", mmc_hostname(host->mmc)); tegra_sdhci_apply_tuning_correction(host, num_tun_iter, upperthreshold, lowerthreshold, fixed_tap); pr_info("%s: Tap value after applying correction %u\n", mmc_hostname(host->mmc), tegra_host->tuned_tap_delay); } else { retain_hw_tun_tap: reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); tegra_host->tuned_tap_delay = ((reg >> SDHCI_CLOCK_CTRL_TAP_SHIFT) & SDHCI_CLOCK_CTRL_TAP_MASK); } tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay, SET_REQ_TAP); tegra_host->tuning_status = TUNING_STATUS_DONE; pr_info("%s: hw tuning done ...\n", mmc_hostname(host->mmc)); tegra_sdhci_dump_vendor_regs(host); } static void tegra_sdhci_vendor_trim_clear_sel_vreg(struct sdhci_host *host, bool enable) { unsigned int misc_ctrl; misc_ctrl = sdhci_readl(host, SDMMC_VNDR_IO_TRIM_CTRL_0); if (enable) { misc_ctrl &= ~(SDMMC_VNDR_IO_TRIM_CTRL_0_SEL_VREG_MASK); sdhci_writel(host, misc_ctrl, SDMMC_VNDR_IO_TRIM_CTRL_0); udelay(3); sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); } else { misc_ctrl |= (SDMMC_VNDR_IO_TRIM_CTRL_0_SEL_VREG_MASK); sdhci_writel(host, misc_ctrl, SDMMC_VNDR_IO_TRIM_CTRL_0); udelay(1); } } static void tegra_sdhci_mask_host_caps(struct sdhci_host *host, u8 uhs_mask) { /* Mask any bus speed modes if set in platform data */ if (uhs_mask & MMC_UHS_MASK_SDR12) host->mmc->caps &= ~MMC_CAP_UHS_SDR12; if (uhs_mask & MMC_UHS_MASK_SDR25) host->mmc->caps &= ~MMC_CAP_UHS_SDR25; if (uhs_mask & MMC_UHS_MASK_SDR50) host->mmc->caps &= ~MMC_CAP_UHS_SDR50; if (uhs_mask & MMC_UHS_MASK_SDR104) host->mmc->caps &= ~MMC_CAP_UHS_SDR104; if (uhs_mask & MMC_UHS_MASK_DDR50) { host->mmc->caps &= ~MMC_CAP_UHS_DDR50; host->mmc->caps &= ~MMC_CAP_1_8V_DDR; } if (uhs_mask & MMC_MASK_HS200) { host->mmc->caps2 &= ~MMC_CAP2_HS200; host->mmc->caps2 &= ~MMC_CAP2_HS400; host->mmc->caps2 &= ~MMC_CAP2_HS400_ES; } if (uhs_mask & MMC_MASK_HS400) { host->mmc->caps2 &= ~MMC_CAP2_HS400; host->mmc->caps2 &= ~MMC_CAP2_HS400_ES; } if (uhs_mask & MMC_MASK_SD_HS) host->mmc->caps &= ~MMC_CAP_SD_HIGHSPEED; } static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; u32 misc_ctrl, clk_ctrl, misc_ctrl_2; int err; bool clear_ddr_signalling = true; sdhci_reset(host, mask); if (!(mask & SDHCI_RESET_ALL)) return; err = tegra_prod_set_by_name(&host->ioaddr, "prod", tegra_host->prods); if (err) dev_err(mmc_dev(host->mmc), "Failed to set prod-reset settings %d\n", err); /* Set the tap delay value */ if (!tegra_sdhci_skip_retuning(host)) tegra_sdhci_set_tap(host, 0, SET_DEFAULT_TAP); misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | SDHCI_MISC_CTRL_ENABLE_SDR50 | SDHCI_MISC_CTRL_ENABLE_DDR50 | SDHCI_MISC_CTRL_ENABLE_SDR104); /* * If the board does not define a regulator for the SDHCI * IO voltage, then don't advertise support for UHS modes * even if the device supports it because the IO voltage * cannot be configured. */ if (tegra_host->vqmmc_always_on || !IS_ERR(host->mmc->supply.vqmmc)) { /* Erratum: Enable SDHCI spec v3.00 support */ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; /* Advertise UHS modes as supported by host */ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; if ((soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) && !(tegra_host->uhs_mask & MMC_UHS_MASK_DDR50)) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; } if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) { misc_ctrl_2 = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2); tegra_host->slcg_status = !(misc_ctrl_2 & SDHCI_MISC_CTRL_2_CLK_OVR_ON); } else tegra_host->slcg_status = !(clk_ctrl & SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE); sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); /* SEL_VREG should be 0 for all modes*/ tegra_sdhci_vendor_trim_clear_sel_vreg(host, true); if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) tegra_host->pad_calib_required = true; /* ddr signalling post resume */ if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) && ((host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) || (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50))) clear_ddr_signalling = false; if (clear_ddr_signalling) tegra_host->ddr_signaling = false; tegra_sdhci_mask_host_caps(host, tegra_host->uhs_mask); } static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) { u32 ctrl; ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && (bus_width == MMC_BUS_WIDTH_8)) { ctrl &= ~SDHCI_CTRL_4BITBUS; ctrl |= SDHCI_CTRL_8BITBUS; } else { ctrl &= ~SDHCI_CTRL_8BITBUS; if (bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } static void tegra_sdhci_configure_e_input(struct sdhci_host *host, bool enable) { u32 reg; reg = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); if (enable) reg |= SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD_MASK; else reg &= ~SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD_MASK; sdhci_writel(host, reg, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); udelay(1); } static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); u32 val, signal_voltage; u16 clk; int card_clk_enabled, ret; const char *comp_vref; unsigned int timeout = 10; unsigned int timing = host->mmc->ios.timing; if (tegra_host->disable_auto_cal) return; signal_voltage = host->mmc->ios.signal_voltage; comp_vref = (signal_voltage == MMC_SIGNAL_VOLTAGE_330) ? "comp-vref-3v3" : "comp-vref-1v8"; clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); card_clk_enabled = clk & SDHCI_CLOCK_CARD_EN; if (card_clk_enabled) { clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } tegra_sdhci_configure_e_input(host, true); udelay(1); ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_SDMEM_COMP_PADCTRL, SDHCI_TEGRA_SDMEMCOMP_PADCTRL_VREF_SEL); if (ret < 0) dev_err(mmc_dev(host->mmc), "%s: error %d in comp vref settings\n", __func__, ret); /* Enable Auto Calibration*/ ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_AUTO_CAL_CONFIG, SDHCI_AUTO_CAL_ENABLE); if (ret < 0) dev_err(mmc_dev(host->mmc), "%s: error %d in autocal-en settings\n", __func__, ret); val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); val |= SDHCI_AUTO_CAL_START; sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG); /* Program calibration offsets */ ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_AUTO_CAL_CONFIG, SDHCI_AUTO_CAL_PUPD_OFFSETS); if (ret < 0) dev_err(mmc_dev(host->mmc), "error %d in autocal-pu-pd-offset settings\n", ret); /* Wait 2us after auto calibration is enabled */ udelay(2); /* Wait until calibration is done */ do { if (!(sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_STATUS) & SDHCI_TEGRA_AUTO_CAL_ACTIVE)) break; mdelay(1); timeout--; } while (timeout); if (!timeout) dev_err(mmc_dev(host->mmc), "Auto calibration timed out\n"); tegra_sdhci_configure_e_input(host, false); if (card_clk_enabled) { clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } if (tegra_host->en_periodic_calib) { tegra_host->timestamp = ktime_get(); host->timestamp = ktime_get(); host->is_calib_done = true; } } static unsigned long get_nearest_clock_freq(unsigned long parent_rate, unsigned long desired_rate) { unsigned long result, result_frac_div; int div, rem; if (parent_rate <= desired_rate) return parent_rate; div = parent_rate / desired_rate; div = (div > MAX_DIVISOR_VALUE) ? MAX_DIVISOR_VALUE : div; rem = parent_rate % desired_rate; result = parent_rate / div; if (div == MAX_DIVISOR_VALUE || !rem) return (parent_rate / div); else if (result > desired_rate) { result_frac_div = (parent_rate << 1) / ((div << 1) + 1); if (result_frac_div > desired_rate) return (parent_rate / (div + 1)); else return result_frac_div; } return result; } static void tegra_sdhci_set_clk_parent(struct sdhci_host *host, unsigned long desired_rate) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_tegra_clk_src_data *clk_src_data; unsigned long parent_clk_rate, rate, nearest_freq_rate = 0; int rc; int mode = 0; u8 i, sel_parent_idx = 0; if (tegra_platform_is_fpga()) return; clk_src_data = tegra_host->clk_src_data; if (!clk_src_data) { dev_err(mmc_dev(host->mmc), "clk src data NULL"); return; } if (tegra_host->static_parent_clk_mapping) { mode = host->mmc->ios.timing; if (tegra_host->parent_clk_index[mode] != -EINVAL) { sel_parent_idx = tegra_host->parent_clk_index[mode]; goto set_parent; } } for (i = 0; i < clk_src_data->parent_clk_src_cnt; i++) { parent_clk_rate = clk_src_data->parent_clk_rate[i]; rate = get_nearest_clock_freq(parent_clk_rate, desired_rate); if (rate > nearest_freq_rate) { nearest_freq_rate = rate; sel_parent_idx = i; } } set_parent: dev_dbg(mmc_dev(host->mmc), "chosen clk parent %s, parent rate %lu\n", clk_src_data->parent_clk_name[sel_parent_idx], clk_src_data->parent_clk_rate[sel_parent_idx]); /* Do nothing if the desired parent is already set */ if (clk_src_data->curr_parent_clk_idx == sel_parent_idx) return; else { rc = clk_set_parent(pltfm_host->clk, clk_src_data->parent_clk[sel_parent_idx]); if (rc) dev_err(mmc_dev(host->mmc), "Failed to set parent pll %d\n", rc); else clk_src_data->curr_parent_clk_idx = sel_parent_idx; } } static void tegra_sdhci_set_clk_rate(struct sdhci_host *host, unsigned long host_clk) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int rc; if (host_clk == tegra_host->curr_clk_rate) return; /* Set the required clock parent based on the desired rate */ tegra_sdhci_set_clk_parent(host, host_clk); /* * Proceed irrespective of parent selection as the interface could * work at a lower frequency too. Parent clk selection would report * errors in the logs. */ rc = clk_set_rate(pltfm_host->clk, host_clk); if (rc) dev_err(mmc_dev(host->mmc), "Failed to set %lu clk rate\n", host_clk); else { /* * Clock frequency actually set would be slightly different from * desired rate. Next request would again come for the desired * rate. Hence, store the desired rate in curr_clk_rate. */ tegra_host->curr_clk_rate = host_clk; } } static unsigned long tegra_sdhci_apply_clk_limits(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); unsigned long host_clk; if (tegra_host->ddr_signaling) host_clk = (tegra_host->max_ddr_clk_limit) ? tegra_host->max_ddr_clk_limit * 2 : clock * 2; else host_clk = (clock > tegra_host->max_clk_limit) ? tegra_host->max_clk_limit : clock; dev_dbg(mmc_dev(host->mmc), "Setting clk limit %lu\n", host_clk); return host_clk; } static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); unsigned long host_clk; int rc; u8 vndr_ctrl; ktime_t cur_time; s64 period_time; host_clk = tegra_sdhci_apply_clk_limits(host, clock); if (clock) { dev_dbg(mmc_dev(host->mmc), "Enabling clk %u, clk enabled %d\n", clock, host->mmc->is_host_clk_enabled); if (host->mmc->skip_host_clkgate) { sdhci_set_card_clock(host, true); return; } if (!tegra_host->rate_change_needs_clk) tegra_sdhci_set_clk_rate(host, host_clk); /* Enable SDMMC host CAR clock */ if (!host->mmc->is_host_clk_enabled) { rc = clk_prepare_enable(pltfm_host->clk); if (rc) { dev_err(mmc_dev(host->mmc), "clk enable failed %d\n", rc); return; } rc = clk_prepare_enable(tegra_host->tmclk); if (rc) { dev_err(mmc_dev(host->mmc), "timeout clk enable failed %d\n", rc); return; } host->mmc->is_host_clk_enabled = true; vndr_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); vndr_ctrl |= SDHCI_CLOCK_CTRL_SDMMC_CLK; sdhci_writeb(host, vndr_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); /* power up / active state */ tegra_sdhci_vendor_trim_clear_sel_vreg(host, true); if (tegra_host->emc_clk) { rc = tegra_bwmgr_set_emc(tegra_host->emc_clk, SDMMC_EMC_MAX_FREQ, TEGRA_BWMGR_SET_EMC_SHARED_BW); if (rc) dev_err(mmc_dev(host->mmc), "enabling eMC clock failed, err: %d\n", rc); } } /* Set the desired clk freq rate */ if (tegra_host->rate_change_needs_clk) tegra_sdhci_set_clk_rate(host, host_clk); host->max_clk = clk_get_rate(pltfm_host->clk); dev_dbg(mmc_dev(host->mmc), "req clk %lu, set clk %d\n", host_clk, host->max_clk); /* Run auto calibration if required */ if (tegra_host->pad_calib_required) { tegra_sdhci_pad_autocalib(host); tegra_host->pad_calib_required = false; } if (tegra_host->en_periodic_calib && host->is_calib_done) { cur_time = ktime_get(); period_time = ktime_to_ms(ktime_sub(cur_time, tegra_host->timestamp)); if (period_time >= SDHCI_PERIODIC_CALIB_TIMEOUT) tegra_sdhci_pad_autocalib(host); } /* Enable SDMMC internal and card clocks */ sdhci_set_clock(host, clock); } else { dev_dbg(mmc_dev(host->mmc), "Disabling clk %u, clk enabled %d\n", clock, host->mmc->is_host_clk_enabled); if (host->mmc->skip_host_clkgate) { sdhci_set_card_clock(host, false); return; } /* Disable the card and internal clocks first */ sdhci_set_clock(host, clock); /* Disable SDMMC host CAR clock */ if (host->mmc->is_host_clk_enabled) { /* power down / idle state */ tegra_sdhci_vendor_trim_clear_sel_vreg(host, false); vndr_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); vndr_ctrl &= ~SDHCI_CLOCK_CTRL_SDMMC_CLK; sdhci_writeb(host, vndr_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); host->mmc->is_host_clk_enabled = false; clk_disable_unprepare(tegra_host->tmclk); clk_disable_unprepare(pltfm_host->clk); if (tegra_host->emc_clk) { rc = tegra_bwmgr_set_emc(tegra_host->emc_clk, 0, TEGRA_BWMGR_SET_EMC_SHARED_BW); if (rc) dev_err(mmc_dev(host->mmc), "disabling eMC clock failed, err: %d\n", rc); } } } } static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int ret; u8 tap_delay_type; bool tuning_mode = false; bool set_num_tun_iter = false; bool set_trim_delay = false; bool set_padpipe_clk_override = false; bool set_sdmmc_spare_0 = false; tegra_host->ddr_signaling = false; sdhci_set_uhs_signaling(host, timing); switch (timing) { case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR104: tuning_mode = true; break; case MMC_TIMING_MMC_DDR52: set_sdmmc_spare_0 = true; case MMC_TIMING_UHS_DDR50: tegra_host->ddr_signaling = true; set_trim_delay = true; break; case MMC_TIMING_MMC_HS200: tuning_mode = true; set_num_tun_iter = true; set_trim_delay = true; break; case MMC_TIMING_MMC_HS400: tuning_mode = true; set_num_tun_iter = true; set_padpipe_clk_override = true; break; default: break; } if ((timing > tegra_host->timing) && !tegra_host->set_1v8_calib_offsets) { tegra_sdhci_pad_autocalib(host); tegra_host->set_1v8_calib_offsets = true; tegra_host->timing = timing; } /* Set trim delay */ if (set_trim_delay) { ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_VENDOR_CLOCK_CTRL, SDHCI_CLOCK_CTRL_TRIM_MASK << SDHCI_CLOCK_CTRL_TRIM_SHIFT); if (ret < 0) dev_err(mmc_dev(host->mmc), "Failed to set trim value for timing %d, %d\n", timing, ret); } /* Set Tap delay */ if (tegra_host->ddr_signaling) tap_delay_type = SET_DDR_TAP; else if ((tegra_host->tuning_status == TUNING_STATUS_DONE) && tuning_mode) tap_delay_type = SET_REQ_TAP; else tap_delay_type = SET_DEFAULT_TAP; tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay, tap_delay_type); /*set padpipe_clk_override*/ if (set_padpipe_clk_override) { ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_VENDOR_CLOCK_CTRL, SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE); if (ret < 0) dev_err(mmc_dev(host->mmc), "Failed to set padpipe clk override value for timing %d, %d\n", timing, ret); } /* Set number of tuning iterations */ if (set_num_tun_iter) { ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_VNDR_TUN_CTRL0_0, SDHCI_TUN_CTRL0_TUNING_ITER_MASK << SDHCI_TUN_CTRL0_TUNING_ITER_SHIFT); if (ret < 0) dev_err(mmc_dev(host->mmc), "Failed to set number of iterations for timing %d, %d\n", timing, ret); } /* Set SDMMC_SPARE_0*/ if (set_sdmmc_spare_0) { ret = tegra_prod_set_by_name_partially(&host->ioaddr, prod_device_states[timing], tegra_host->prods, 0, SDHCI_TEGRA_VENDOR_MISC_CTRL, SDHCI_MISC_CTRL_SDMMC_SPARE_0_MASK); if (ret < 0) dev_err(mmc_dev(host->mmc), "Failed to set spare0 field for timing %d, %d\n", timing, ret); } } static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); /* * DDR modes require the host to run at double the card frequency, so * the maximum rate we can support is half of the module input clock. */ return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; } static unsigned int tegra_sdhci_get_timeout_clock(struct sdhci_host *host) { /* * Tegra SDMMC controller advertises 12MHz timeout clock. Controller * models in simulator might not advertise the timeout clock frequency. * To avoid errors, return 12MHz clock for supporting timeout clock * on simulators. */ return SDMMC_TIMEOUT_CLK_FREQ_MHZ; } static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap, int type) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; u32 reg; u16 clk; bool card_clk_enabled = false; int err; if (tap > MAX_TAP_VALUE) { dev_err(mmc_dev(host->mmc), "Invalid tap value %d\n", tap); return; } if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) { clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); card_clk_enabled = clk & SDHCI_CLOCK_CARD_EN; if (card_clk_enabled) { clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } } /* Disable HW tap delay config */ if (soc_data->nvquirks & NVQUIRK_HW_TAP_CONFIG) { reg = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); reg &= ~SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP; sdhci_writel(host, reg, SDHCI_VNDR_TUN_CTRL0_0); } if (type & (SET_DDR_TAP | SET_DEFAULT_TAP)) { err = tegra_prod_set_by_name_partially(&host->ioaddr, "prod", tegra_host->prods, 0, SDHCI_TEGRA_VENDOR_CLOCK_CTRL, SDHCI_CLOCK_CTRL_TAP_MASK << SDHCI_CLOCK_CTRL_TAP_SHIFT); if (err < 0) dev_dbg(mmc_dev(host->mmc), "%s: error %d in tap settings, timing: %d\n", __func__, err, host->mmc->ios.timing); } else { reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); reg &= ~(SDHCI_CLOCK_CTRL_TAP_MASK << SDHCI_CLOCK_CTRL_TAP_SHIFT); reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); } /* Enable HW tap delay config */ if (soc_data->nvquirks & NVQUIRK_HW_TAP_CONFIG) { reg = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); reg |= SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP; sdhci_writel(host, reg, SDHCI_VNDR_TUN_CTRL0_0); } if ((soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) && card_clk_enabled) { udelay(1); sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } } static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { unsigned int min, max; /* * Start search for minimum tap value at 10, as smaller values are * may wrongly be reported as working but fail at higher speeds, * according to the TRM. */ min = 10; while (min < 255) { tegra_sdhci_set_tap(host, min, SET_REQ_TAP); if (!mmc_send_tuning(host->mmc, opcode, NULL)) break; min++; } /* Find the maximum tap value that still passes. */ max = min + 1; while (max < 255) { tegra_sdhci_set_tap(host, max, SET_REQ_TAP); if (mmc_send_tuning(host->mmc, opcode, NULL)) { max--; break; } max++; } /* The TRM states the ideal tap value is at 75% in the passing range. */ tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4), SET_REQ_TAP); return mmc_send_tuning(host->mmc, opcode, NULL); } static void tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; if (!tegra_host->pwrdet_support || IS_ERR_OR_NULL(tegra_host->pinctrl_sdmmc) || IS_ERR_OR_NULL(tegra_host->e_33v_enable) || IS_ERR_OR_NULL(tegra_host->e_33v_disable)) { dev_dbg(mmc_dev(host->mmc), "IO pad Volt setting skip: pwrdet-%d, pinctrl_sdmmc-%d" " e_33v_enable-%d, e_33v_disable-%d\n", tegra_host->pwrdet_support, IS_ERR_OR_NULL(tegra_host->pinctrl_sdmmc), IS_ERR_OR_NULL(tegra_host->e_33v_enable), IS_ERR_OR_NULL(tegra_host->e_33v_disable)); return; } if (voltage == SDHOST_LOW_VOLT_MIN) { ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, tegra_host->e_33v_disable); if (ret < 0) dev_warn(mmc_dev(host->mmc), "setting E_33V dis failed, ret: %d\n", ret); } else { ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, tegra_host->e_33v_enable); if (ret < 0) dev_warn(mmc_dev(host->mmc), "setting E_33V en failed, ret: %d\n", ret); } } static void tegra_sdhci_signal_voltage_switch_pre(struct sdhci_host *host, int signal_voltage) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); /* Toggle power gpio for switching voltage on FPGA */ if (gpio_is_valid(tegra_host->volt_switch_gpio)) { if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) { gpio_set_value(tegra_host->volt_switch_gpio, 1); dev_info(mmc_dev(host->mmc), "3.3V set by voltage switch gpio\n"); } else { gpio_set_value(tegra_host->volt_switch_gpio, 0); dev_info(mmc_dev(host->mmc), "1.8V set by voltage switch gpio\n"); mdelay(1000); } return; } /* For 3.3V, pwrdet should be set before setting the voltage */ if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) tegra_sdhci_set_padctrl(host, 3300000); } static void tegra_sdhci_update_sdmmc_pinctrl_register(struct sdhci_host *sdhci, bool set) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; struct pinctrl_state *set_schmitt[2]; int ret; int i; if (!(soc_data->nvquirks & NVQUIRK_UPDATE_PIN_CNTRL_REG)) return; if (set) { set_schmitt[0] = tegra_host->schmitt_enable[0]; set_schmitt[1] = tegra_host->schmitt_enable[1]; if (!IS_ERR_OR_NULL(tegra_host->drv_code_strength)) { ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, tegra_host->drv_code_strength); if (ret < 0) dev_warn(mmc_dev(sdhci->mmc), "setting drive code strength failed\n"); } } else { set_schmitt[0] = tegra_host->schmitt_disable[0]; set_schmitt[1] = tegra_host->schmitt_disable[1]; if (!IS_ERR_OR_NULL(tegra_host->default_drv_code_strength)) { ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, tegra_host->default_drv_code_strength); if (ret < 0) dev_warn(mmc_dev(sdhci->mmc), "setting default drive code strength failed\n"); } } for (i = 0; i < 2; i++) { if (IS_ERR_OR_NULL(set_schmitt[i])) continue; ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, set_schmitt[i]); if (ret < 0) dev_warn(mmc_dev(sdhci->mmc), "setting schmitt state failed\n"); } } static void tegra_sdhci_signal_voltage_switch_post(struct sdhci_host *host, int signal_voltage) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int ret; if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) { tegra_sdhci_set_padctrl(host, 1800000); tegra_sdhci_update_sdmmc_pinctrl_register(host, true); /* * prod_c_1_8v and prod_c_3_3v are applicable to only * t194 platforms */ ret = tegra_prod_set_by_name_partially(&host->ioaddr, "prod_c_1_8v", tegra_host->prods, 0, SDHCI_TEGRA_SDMEM_COMP_PADCTRL, SDHCI_TEGRA_SDMEMCOMP_PADCTRL_DRVUP_OVR); if (ret < 0) dev_dbg(mmc_dev(host->mmc), "%s: error %d in comp drvup settings at 1.8v\n", __func__, ret); } else { tegra_sdhci_update_sdmmc_pinctrl_register(host, false); ret = tegra_prod_set_by_name_partially(&host->ioaddr, "prod_c_3_3v", tegra_host->prods, 0, SDHCI_TEGRA_SDMEM_COMP_PADCTRL, SDHCI_TEGRA_SDMEMCOMP_PADCTRL_DRVUP_OVR); if (ret < 0) dev_dbg(mmc_dev(host->mmc), "%s: error %d in comp drvup settings at 3.3v\n", __func__, ret); } if (tegra_host->pad_calib_required) tegra_sdhci_pad_autocalib(host); } static void tegra_sdhci_voltage_switch(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) tegra_host->pad_calib_required = true; } static int sdhci_tegra_get_parent_pll_from_dt(struct sdhci_host *host, struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct device_node *np = pdev->dev.of_node; struct sdhci_tegra_clk_src_data *clk_src_data; struct clk *parent_clk; const char *pll_str; int i, cnt, j = 0; if (!np || !tegra_host) return -EINVAL; if (!of_find_property(np, "pll_source", NULL)) return -ENXIO; clk_src_data = tegra_host->clk_src_data; cnt = of_property_count_strings(np, "pll_source"); if (!cnt) return -EINVAL; if (cnt > MAX_CLK_PARENTS) { dev_warn(mmc_dev(host->mmc), "Parent sources list exceeded limit\n"); cnt = MAX_CLK_PARENTS; } for (i = 0; i < cnt; i++) { of_property_read_string_index(np, "pll_source", i, &pll_str); parent_clk = devm_clk_get(&pdev->dev, pll_str); if (IS_ERR(parent_clk)) dev_err(mmc_dev(host->mmc), "Failed to get %s clk\n", pll_str); else { clk_src_data->parent_clk_name[j] = pll_str; clk_src_data->parent_clk_rate[j] = clk_get_rate(parent_clk); clk_src_data->parent_clk[j++] = parent_clk; } } /* Count valid parent clock sources with clk structures */ clk_src_data->parent_clk_src_cnt = j; /* * Set current parent clock index to -1 to force parent clock setting. */ clk_src_data->curr_parent_clk_idx = -1; return 0; } static void tegra_sdhci_init_pinctrl_info(struct device *dev, struct sdhci_tegra *tegra_host) { struct device_node *np = dev->of_node; int ret, i; if (!np) return; tegra_host->prods = devm_tegra_prod_get(dev); if (IS_ERR_OR_NULL(tegra_host->prods)) { dev_err(dev, "Prod-setting not available\n"); tegra_host->prods = NULL; } if (tegra_host->pwrdet_support) { tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev); if (IS_ERR_OR_NULL(tegra_host->pinctrl_sdmmc)) { dev_err(dev, "Missing pinctrl info, err: %ld\n", PTR_ERR(tegra_host->pinctrl_sdmmc)); tegra_host->pinctrl_sdmmc = NULL; return; } tegra_host->e_33v_enable = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_e_33v_enable"); if (IS_ERR_OR_NULL(tegra_host->e_33v_enable)) dev_dbg(dev, "Missing E_33V enable state, err:%ld\n", PTR_ERR(tegra_host->e_33v_enable)); tegra_host->e_33v_disable = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_e_33v_disable"); if (IS_ERR_OR_NULL(tegra_host->e_33v_disable)) dev_dbg(dev, "Missing E_33V disable state, err:%ld\n", PTR_ERR(tegra_host->e_33v_disable)); } if (tegra_host->update_pinctrl_settings) { tegra_host->schmitt_enable[0] = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_schmitt_enable"); if (IS_ERR_OR_NULL(tegra_host->schmitt_enable[0])) dev_err(dev, "Missing schmitt enable state\n"); tegra_host->schmitt_enable[1] = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_clk_schmitt_enable"); if (IS_ERR_OR_NULL(tegra_host->schmitt_enable[1])) dev_err(dev, "Missing clk schmitt enable state\n"); tegra_host->schmitt_disable[0] = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_schmitt_disable"); if (IS_ERR_OR_NULL(tegra_host->schmitt_disable[0])) dev_err(dev, "Missing schmitt disable state\n"); tegra_host->schmitt_disable[1] = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_clk_schmitt_disable"); if (IS_ERR_OR_NULL(tegra_host->schmitt_disable[1])) dev_err(dev, "Missing clk schmitt disable state\n"); for (i = 0; i < 2; i++) { if (!IS_ERR_OR_NULL(tegra_host->schmitt_disable[i])) { ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, tegra_host->schmitt_disable[i]); if (ret < 0) dev_warn(dev, "setting schmitt state failed\n"); } } tegra_host->drv_code_strength = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_drv_code"); if (IS_ERR_OR_NULL(tegra_host->drv_code_strength)) dev_err(dev, "Missing sdmmc drive code state\n"); tegra_host->default_drv_code_strength = pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc_default_drv_code"); if (IS_ERR_OR_NULL(tegra_host->default_drv_code_strength)) dev_err(dev, "Missing sdmmc default drive code state\n"); } } static void tegra_sdhci_pre_regulator_config(struct sdhci_host *sdhci, int vdd, bool flag) { if (!vdd) return; tegra_sdhci_configure_e_input(sdhci, flag); } /* Configure voltage switch specific requirements */ static void tegra_sdhci_voltage_switch_req(struct sdhci_host *host, bool req) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; u32 clk_ctrl; if (!req) { /* Disable SLCG */ clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); clk_ctrl = clk_ctrl | SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE; sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) { clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2); clk_ctrl = clk_ctrl | SDHCI_MISC_CTRL_2_CLK_OVR_ON; sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL_2); } } else { /* Restore SLCG */ if (tegra_host->slcg_status) { clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); clk_ctrl = clk_ctrl & ~SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE; sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) { clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2); clk_ctrl = clk_ctrl & ~SDHCI_MISC_CTRL_2_CLK_OVR_ON; sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL_2); } } } } static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_b = tegra_sdhci_readb, .read_w = tegra_sdhci_readw, .read_l = tegra_sdhci_readl, .write_b = tegra_sdhci_writeb, .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, .set_clock = tegra_sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, .get_max_clock = tegra_sdhci_get_max_clock, .get_timeout_clock = tegra_sdhci_get_timeout_clock, .get_max_tuning_loop_counter = tegra_sdhci_get_max_tuning_loop_counter, .skip_retuning = tegra_sdhci_skip_retuning, .post_tuning = tegra_sdhci_post_tuning, .voltage_switch_pre = tegra_sdhci_signal_voltage_switch_pre, .voltage_switch_post = tegra_sdhci_signal_voltage_switch_post, .hs400_enhanced_strobe = tegra_sdhci_hs400_enhanced_strobe, .post_init = tegra_sdhci_post_init, .suspend = tegra_sdhci_suspend, .resume = tegra_sdhci_resume, .complete = tegra_sdhci_complete, .runtime_suspend = tegra_sdhci_runtime_suspend, .runtime_resume = tegra_sdhci_runtime_resume, .platform_resume = tegra_sdhci_post_resume, .card_event = tegra_sdhci_card_event, .dump_vendor_regs = tegra_sdhci_dump_vendor_regs, .pre_regulator_config = tegra_sdhci_pre_regulator_config, .voltage_switch_req = tegra_sdhci_voltage_switch_req, .pad_autocalib = tegra_sdhci_pad_autocalib, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &tegra_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra20 = { .pdata = &sdhci_tegra20_pdata, .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | NVQUIRK_ENABLE_BLOCK_GAP_DET, }; static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_HS200, .ops = &tegra_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra30 = { .pdata = &sdhci_tegra30_pdata, .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_SDR104 | NVQUIRK_HAS_PADCALIB, }; static const struct sdhci_ops tegra114_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_b = tegra_sdhci_writeb, .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, .set_clock = tegra_sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, .platform_execute_tuning = tegra_sdhci_execute_tuning, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, .get_max_clock = tegra_sdhci_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_SEL_SDR104_UHS_MODE_IN_SDR50, .ops = &tegra114_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, }; static const struct sdhci_pltfm_data sdhci_tegra124_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | /* * The TRM states that the SD/MMC controller found on * Tegra124 can address 34 bits (the maximum supported by * the Tegra memory controller), but tests show that DMA * to or from above 4 GiB doesn't work. This is possibly * caused by missing programming, though it's not obvious * what sequence is required. Mark 64-bit DMA broken for * now to fix this for existing users (e.g. Nyan boards). */ SDHCI_QUIRK2_BROKEN_64_BIT_DMA | SDHCI_QUIRK2_SEL_SDR104_UHS_MODE_IN_SDR50, .ops = &tegra114_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra124 = { .pdata = &sdhci_tegra124_pdata, }; static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_USE_64BIT_ADDR | SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER | SDHCI_QUIRK2_SEL_SDR104_UHS_MODE_IN_SDR50 | SDHCI_QUIRK2_NO_CALC_MAX_BUSY_TO | SDHCI_QUIRK2_NON_STD_TUN_CARD_CLOCK | SDHCI_QUIRK2_HOST_OFF_CARD_ON, .ops = &tegra_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra210 = { .pdata = &sdhci_tegra210_pdata, .nvquirks = NVQUIRK_HW_TAP_CONFIG | NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | NVQUIRK_READ_REG_AFTER_WRITE | NVQUIRK_ENABLE_SDHCI_SPEC_300 | NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_DDR50 | NVQUIRK_ENABLE_SDR104 | NVQUIRK_UPDATE_PIN_CNTRL_REG | NVQUIRK_HAS_PADCALIB | SDHCI_MISC_CTRL_ENABLE_SDR50, }; static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_USE_64BIT_ADDR | SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_SEL_SDR104_UHS_MODE_IN_SDR50, .ops = &tegra_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra186 = { .pdata = &sdhci_tegra186_pdata, .nvquirks = NVQUIRK_HW_TAP_CONFIG | NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_DDR50 | NVQUIRK_ENABLE_SDR104 | NVQUIRK_SDMMC_CLK_OVERRIDE | SDHCI_MISC_CTRL_ENABLE_SDR50 | NVQUIRK_HAS_PADCALIB, .cqequirks = CMDQ_QUIRK_SET_CMD_TIMING_R1B_DCMD, }; static const struct sdhci_pltfm_data sdhci_tegra194_pdata = { .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_USE_64BIT_ADDR | SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_NON_STD_TUN_CARD_CLOCK, .ops = &tegra_sdhci_ops, }; static const struct sdhci_tegra_soc_data soc_data_tegra194 = { .pdata = &sdhci_tegra194_pdata, .nvquirks = NVQUIRK_HW_TAP_CONFIG | NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_DDR50 | NVQUIRK_ENABLE_SDR104 | NVQUIRK_SDMMC_CLK_OVERRIDE | SDHCI_MISC_CTRL_ENABLE_SDR50, }; static const struct of_device_id sdhci_tegra_dt_match[] = { { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 }, { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, {} }; MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); static int sdhci_tegra_parse_dt(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); int val; if (!np) return -EINVAL; of_property_read_u32(np, "max-clk-limit", (u32 *)&tegra_host->max_clk_limit); of_property_read_u32(np, "ddr-clk-limit", (u32 *)&tegra_host->max_ddr_clk_limit); tegra_host->pwrdet_support = of_property_read_bool(np, "pwrdet-support"); tegra_host->update_pinctrl_settings = of_property_read_bool(np, "nvidia,update-pinctrl-settings"); tegra_host->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); tegra_host->cd_wakeup_capable = of_property_read_bool(np, "nvidia,cd-wakeup-capable"); #ifdef CONFIG_MMC_CQ_HCI tegra_host->enable_hwcq = of_property_read_bool(np, "nvidia,enable-hwcq"); #endif host->ocr_mask = MMC_VDD_27_36 | MMC_VDD_165_195; tegra_host->instance = of_alias_get_id(np, "sdhci"); if (!of_property_read_u32(np, "mmc-ocr-mask", &val)) { if (val == 1) host->ocr_mask &= ~(MMC_VDD_26_27 | MMC_VDD_27_28); else if (val == 2) host->ocr_mask &= (MMC_VDD_32_33 | MMC_VDD_165_195); else if (val == 3) host->ocr_mask &= (MMC_VDD_33_34 | MMC_VDD_165_195); } tegra_host->rate_change_needs_clk = of_property_read_bool(np, "nvidia,clk-en-before-freq-update"); tegra_host->volt_switch_gpio = of_get_named_gpio(np, "nvidia,voltage-switch-gpio", 0); tegra_host->en_periodic_cflush = of_property_read_bool(np, "nvidia,en-periodic-cflush"); tegra_host->static_parent_clk_mapping = of_property_read_bool(np, "nvidia,set-parent-clk"); host->mmc->cd_cap_invert = of_property_read_bool(np, "cd-inverted"); if (tegra_host->en_periodic_cflush) { val = 0; of_property_read_u32(np, "nvidia,periodic-cflush-to", &val); host->mmc->flush_timeout = val; if (val == 0) { tegra_host->en_periodic_cflush = false; dev_warn(&pdev->dev, "Periodic cache flush feature disabled," "since flush timeout value is zero.\n"); } } of_property_read_u32(np, "uhs-mask", (u32 *)&tegra_host->uhs_mask); of_property_read_u32(np, "nvidia,boot-detect-delay", &tegra_host->boot_detect_delay); tegra_host->force_non_rem_rescan = of_property_read_bool(np, "force-non-removable-rescan"); tegra_host->disable_rtpm = of_property_read_bool(np, "nvidia,disable-rtpm"); if (tegra_host->disable_rtpm) { tegra_host->disable_clk_gate = true; #ifdef CONFIG_PM dev_info(&pdev->dev, "runtime pm disabled\n"); #endif } else { tegra_host->disable_clk_gate = of_property_read_bool(np, "disable-dynamic-clock-gating"); #ifdef CONFIG_PM if (tegra_host->disable_clk_gate) dev_info(&pdev->dev, "clock gating disabled\n"); #endif } tegra_host->vqmmc_always_on = of_property_read_bool(np, "nvidia,vqmmc-always-on"); tegra_host->vmmc_always_on = of_property_read_bool(np, "nvidia,vmmc-always-on"); tegra_host->en_periodic_calib = of_property_read_bool(np, "nvidia,en-periodic-calib"); of_property_read_u32(np, "nvidia,min-tap-delay", &tegra_host->min_tap_delay); of_property_read_u32(np, "nvidia,max-tap-delay", &tegra_host->max_tap_delay); return 0; } static int sdhci_tegra_parse_parent_list_from_dt(struct platform_device *pdev, struct sdhci_tegra *tegra_host) { struct device_node *np = pdev->dev.of_node; const char *curr_mode_parent_clk[MMC_TIMING_COUNTER]; const char *pll_str; int i, j, cnt; if (!np) return -EINVAL; if (!of_find_property(np, "nvidia,parent_clk_list", NULL)) return -ENXIO; cnt = of_property_count_strings(np, "nvidia,parent_clk_list"); if (cnt != MMC_TIMING_COUNTER) return -EINVAL; for (i = 0; i < cnt; i++) { of_property_read_string_index(np, "nvidia,parent_clk_list", i, &pll_str); curr_mode_parent_clk[i] = pll_str; /* Initialize parent clock index array with invalid */ tegra_host->parent_clk_index[i] = -EINVAL; } for (i = 0; i < tegra_host->clk_src_data->parent_clk_src_cnt; i++) { for (j = 0; j < MMC_TIMING_COUNTER; j++) { if (!strcmp(curr_mode_parent_clk[j], tegra_host->clk_src_data->parent_clk_name[i])) tegra_host->parent_clk_index[j] = i; } } return 0; } static void sdhci_delayed_detect(struct work_struct *work) { struct sdhci_tegra *tegra_host; struct sdhci_host *host; tegra_host = container_of(work, struct sdhci_tegra, detect_delay.work); host = tegra_host->host; if (sdhci_add_host(host)) goto err_add_host; if (!tegra_host->disable_rtpm) { pm_runtime_mark_last_busy(mmc_dev(host->mmc)); pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } /* Initialize debugfs */ sdhci_tegra_debugfs_init(host); return; err_add_host: if (!tegra_host->disable_rtpm) pm_runtime_disable(mmc_dev(host->mmc)); } static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct sdhci_tegra_soc_data *soc_data; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_tegra *tegra_host; struct clk *clk; struct sdhci_tegra_clk_src_data *clk_src_data; int rc; match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); if (!match) return -EINVAL; soc_data = match->data; host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); tegra_host = sdhci_pltfm_priv(pltfm_host); tegra_host->ddr_signaling = false; tegra_host->pad_calib_required = false; tegra_host->host = host; INIT_DELAYED_WORK(&tegra_host->detect_delay, sdhci_delayed_detect); /* FIXME: This is for until dma-mask binding is supported in DT. * Set coherent_dma_mask for each Tegra SKUs. * If dma_mask is NULL, set it to coherent_dma_mask. */ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; tegra_host->soc_data = soc_data; rc = mmc_of_parse(host->mmc); if (rc) goto err_parse_dt; sdhci_tegra_parse_dt(pdev); clk_src_data = devm_kzalloc(&pdev->dev, sizeof(*clk_src_data), GFP_KERNEL); if (IS_ERR_OR_NULL(clk_src_data)) { dev_err(mmc_dev(host->mmc), "Insufficient memory for clk source data\n"); return -ENOMEM; } tegra_host->clk_src_data = clk_src_data; rc = sdhci_tegra_get_parent_pll_from_dt(host, pdev); if (rc) dev_err(mmc_dev(host->mmc), "Failed to find parent clocks\n"); if (tegra_host->static_parent_clk_mapping) { rc = sdhci_tegra_parse_parent_list_from_dt(pdev, tegra_host); if (rc) { tegra_host->static_parent_clk_mapping = false; dev_err(mmc_dev(host->mmc), "Failed to find parent clocks %d\n", rc); } } tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(tegra_host->power_gpio)) { rc = PTR_ERR(tegra_host->power_gpio); goto err_power_req; } clk = devm_clk_get(&pdev->dev, "sdmmc"); if (IS_ERR(clk)) { dev_err(mmc_dev(host->mmc), "clk err\n"); rc = PTR_ERR(clk); goto err_clk_get; } pltfm_host->clk = clk; tegra_host->tmclk = devm_clk_get(&pdev->dev, "sdmmc_legacy_tm"); if (IS_ERR(tegra_host->tmclk)) { dev_err(mmc_dev(host->mmc), "timeout clk error\n"); rc = PTR_ERR(tegra_host->tmclk); goto err_clk_get; }; clk_set_rate(tegra_host->tmclk, SDMMC_TIMEOUT_CLK_FREQ_HZ); tegra_host->emc_clk = tegra_bwmgr_register(sdmmc_emc_clinet_id[tegra_host->instance]); if (IS_ERR_OR_NULL(tegra_host->emc_clk)) dev_err(mmc_dev(host->mmc), "Client registration for eMC failed\n"); else dev_info(mmc_dev(host->mmc), "Client registration for eMC Successful\n"); tegra_host->rst = devm_reset_control_get(&pdev->dev, "sdhci"); if (IS_ERR(tegra_host->rst)) dev_err(mmc_dev(host->mmc), "reset err\n"); else reset_control_reset(tegra_host->rst); if (tegra_host->disable_rtpm) { tegra_sdhci_set_clock(host, SDMMC_TEGRA_FALLBACK_CLK_HZ); host->mmc->is_host_clk_enabled = true; } else { if (tegra_host->disable_clk_gate) { tegra_sdhci_set_clock(host, SDMMC_TEGRA_FALLBACK_CLK_HZ); host->mmc->is_host_clk_enabled = true; } pm_runtime_enable(mmc_dev(host->mmc)); pm_runtime_get_sync(mmc_dev(host->mmc)); /* * set autosuspend delay results in suspend call if * set active is called before pm_runtime_enable */ pm_runtime_set_autosuspend_delay(mmc_dev(host->mmc), SDHCI_RTPM_MSEC_TMOUT); pm_runtime_use_autosuspend(mmc_dev(host->mmc)); } if (gpio_is_valid(tegra_host->volt_switch_gpio)) { rc = gpio_request(tegra_host->volt_switch_gpio, "sdhci_power"); if (rc) dev_err(mmc_dev(host->mmc), "failed to allocate gpio for voltage switch, " "err: %d\n", rc); gpio_direction_output(tegra_host->volt_switch_gpio, 1); gpio_set_value(tegra_host->volt_switch_gpio, 1); dev_info(mmc_dev(host->mmc), "3.3V set initially by voltage switch gpio\n"); } if (gpio_is_valid(tegra_host->cd_gpio) && tegra_host->cd_wakeup_capable) { tegra_host->cd_irq = gpio_to_irq(tegra_host->cd_gpio); if (tegra_host->cd_irq <= 0) { dev_err(mmc_dev(host->mmc), "failed to get gpio irq %d\n", tegra_host->cd_irq); tegra_host->cd_irq = 0; } else { device_init_wakeup(&pdev->dev, 1); dev_info(mmc_dev(host->mmc), "wakeup init done, cdirq %d\n", tegra_host->cd_irq); } } /* * If there is no card detect gpio, assume that the * card is always present. */ if (!gpio_is_valid(tegra_host->cd_gpio)) { host->mmc->rem_card_present = 1; } else { if (!host->mmc->cd_cap_invert) host->mmc->rem_card_present = (mmc_gpio_get_cd(host->mmc) == 0); else host->mmc->rem_card_present = mmc_gpio_get_cd(host->mmc); } if (!en_boot_part_access) host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC; if (tegra_host->en_periodic_cflush) host->mmc->caps2 |= MMC_CAP2_PERIODIC_CACHE_FLUSH; if (tegra_host->vmmc_always_on) host->mmc->caps2 |= MMC_CAP2_SLOT_REG_ALWAYS_ON; host->mmc->caps2 |= MMC_CAP2_EN_CLK_TO_ACCESS_REG; host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; if (tegra_host->force_non_rem_rescan) host->mmc->caps2 |= MMC_CAP2_FORCE_RESCAN; #ifdef CONFIG_MMC_CQ_HCI if (tegra_host->enable_hwcq) { host->mmc->caps2 |= MMC_CAP2_HW_CQ; host->cq_host = cmdq_pltfm_init(pdev); if (IS_ERR(host->cq_host)) { pr_err("CMDQ: Error in cmdq_platfm_init function\n"); } else { pr_info("CMDQ: cmdq_platfm_init successful\n"); host->cq_host->quirks = soc_data->cqequirks; } } #endif if (tegra_host->en_periodic_calib) host->quirks2 |= SDHCI_QUIRK2_PERIODIC_CALIBRATION; schedule_delayed_work(&tegra_host->detect_delay, msecs_to_jiffies(tegra_host->boot_detect_delay)); return 0; err_clk_get: err_power_req: err_parse_dt: sdhci_pltfm_free(pdev); return rc; } static int tegra_sdhci_suspend(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); /* Enable wake irq at end of suspend */ if (device_may_wakeup(&pdev->dev)) { if (enable_irq_wake(tegra_host->cd_irq)) { dev_err(mmc_dev(host->mmc), "Failed to enable wake irq %u\n", tegra_host->cd_irq); tegra_host->wake_enable_failed = true; } } tegra_sdhci_set_clock(host, 0); host->is_calib_done = false; return 0; } static int tegra_sdhci_resume(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); int ret = 0; if (device_may_wakeup(&pdev->dev)) { if (!tegra_host->wake_enable_failed) { ret = disable_irq_wake(tegra_host->cd_irq); if (ret) dev_err(mmc_dev(host->mmc), "Failed to disable wakeirq %u,err %d\n", tegra_host->cd_irq, ret); } } if (gpio_is_valid(tegra_host->cd_gpio)) { if (!host->mmc->cd_cap_invert) host->mmc->rem_card_present = (mmc_gpio_get_cd(host->mmc) == 0); else host->mmc->rem_card_present = mmc_gpio_get_cd(host->mmc); } else { host->mmc->rem_card_present = true; } if (host->mmc->rem_card_present == false) tegra_host->tuning_status = TUNING_STATUS_RETUNE; /* Set min identificaion clock of 400 KHz */ tegra_sdhci_set_clock(host, 400000); return ret; } static void tegra_sdhci_complete(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); if (!tegra_host->disable_rtpm) { pm_runtime_set_active(mmc_dev(host->mmc)); pm_runtime_enable(mmc_dev(host->mmc)); dev_dbg(mmc_dev(host->mmc), "resume complete runtime enable\n"); } } static int tegra_sdhci_runtime_suspend(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); /* Disable clock */ if (!tegra_host->disable_clk_gate) tegra_sdhci_set_clock(host, 0); return 0; } static int tegra_sdhci_runtime_resume(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); unsigned int clk; if (!tegra_host->disable_clk_gate) { /* Clock enable should be invoked with a non-zero freq */ if (host->clock) clk = host->clock; else if (host->mmc->ios.clock) clk = host->mmc->ios.clock; else clk = SDMMC_TEGRA_FALLBACK_CLK_HZ; tegra_sdhci_set_clock(host, clk); } return 0; } static void tegra_sdhci_post_resume(struct sdhci_host *host) { bool dll_calib_req = false; dll_calib_req = (host->mmc->card && mmc_card_mmc(host->mmc->card) && (host->mmc->ios.timing == MMC_TIMING_MMC_HS400)); if (dll_calib_req) tegra_sdhci_post_init(host); } static int sdhci_tegra_card_detect(struct sdhci_host *host, bool req) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); bool card_present = false; int err = 0; if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) if (host->mmc->rem_card_present) card_present = true; /* Check if card is inserted physically before performing */ if (gpio_is_valid(tegra_host->cd_gpio)) { if ((mmc_gpio_get_cd(host->mmc) == 1) && (!host->mmc->cd_cap_invert)) { err = -ENXIO; dev_err(mmc_dev(host->mmc), "Card not inserted in slot\n"); goto err_config; } else if ((mmc_gpio_get_cd(host->mmc) == 0) && (host->mmc->cd_cap_invert)) { err = -ENXIO; dev_err(mmc_dev(host->mmc), "Card not inserted in slot\n"); goto err_config; } } /* Ignore the request if card already in requested state */ if (card_present == req) { dev_info(mmc_dev(host->mmc), "Card already in requested state\n"); goto err_config; } else { card_present = req; } if (card_present) { /* Virtual card insertion */ host->mmc->rem_card_present = true; host->mmc->rescan_disable = 0; /* If vqmmc regulator and no 1.8V signalling, * then there's no UHS */ if (!IS_ERR(host->mmc->supply.vqmmc)) { err = regulator_enable(host->mmc->supply.vqmmc); if (err) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(host->mmc), err); host->mmc->supply.vqmmc = ERR_PTR(-EINVAL); goto err_config; } tegra_host->is_rail_enabled = true; } /* If vmmc regulator and no 1.8V signalling, * then there's no UHS */ if (!IS_ERR(host->mmc->supply.vmmc)) { err = regulator_enable(host->mmc->supply.vmmc); if (err) { pr_warn("%s: Failed to enable vmmc regulator; %d\n", mmc_hostname(host->mmc), err); host->mmc->supply.vmmc = ERR_PTR(-EINVAL); goto err_config; } tegra_host->is_rail_enabled = true; } } else { /* Virtual card removal */ host->mmc->rem_card_present = false; host->mmc->rescan_disable = 0; if (tegra_host->is_rail_enabled) { if (!IS_ERR(host->mmc->supply.vqmmc)) regulator_disable(host->mmc->supply.vqmmc); if (!IS_ERR(host->mmc->supply.vmmc)) regulator_disable(host->mmc->supply.vmmc); tegra_host->is_rail_enabled = false; } } host->mmc->trigger_card_event = true; mmc_detect_change(host->mmc, msecs_to_jiffies(200)); err_config: return err; } static int get_card_insert(void *data, u64 *val) { struct sdhci_host *host = data; *val = host->mmc->rem_card_present; return 0; } static int set_card_insert(void *data, u64 val) { struct sdhci_host *host = data; int err = 0; if (val > 1) { err = -EINVAL; dev_err(mmc_dev(host->mmc), "Usage error. Use 0 to remove, 1 to insert %d\n", err); goto err_detect; } if (host->mmc->caps & MMC_CAP_NONREMOVABLE) { err = -EINVAL; dev_err(mmc_dev(host->mmc), "usage error, Supports SDCARD hosts only %d\n", err); goto err_detect; } err = sdhci_tegra_card_detect(host, val); err_detect: return err; } DEFINE_SIMPLE_ATTRIBUTE(sdhci_tegra_card_insert_fops, get_card_insert, set_card_insert, "%llu\n"); static void sdhci_tegra_debugfs_init(struct sdhci_host *host) { #ifdef CONFIG_DEBUG_FS struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_tegra_clk_src_data *clk_src_data; struct dentry *sdhcidir, *clkdir, *retval; clk_src_data = tegra_host->clk_src_data; sdhcidir = debugfs_create_dir(dev_name(mmc_dev(host->mmc)), NULL); if (!sdhcidir) { dev_err(mmc_dev(host->mmc), "Failed to create debugfs\n"); return; } /* Create clock debugfs dir under sdhci debugfs dir */ clkdir = debugfs_create_dir("clock_data", sdhcidir); if (!clkdir) goto err; retval = debugfs_create_bool("slcg_status", S_IRUGO, clkdir, &tegra_host->slcg_status); if (!retval) goto err; retval = debugfs_create_ulong("curr_clk_rate", S_IRUGO, clkdir, &tegra_host->curr_clk_rate); if (!retval) goto err; retval = debugfs_create_ulong("parent_clk_rate", S_IRUGO, clkdir, &clk_src_data->parent_clk_rate[ clk_src_data->curr_parent_clk_idx]); if (!retval) goto err; /* backup original host timing capabilities as * debugfs may override it later */ host->caps_timing_orig = host->mmc->caps & (MMC_CAP_SD_HIGHSPEED | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104); retval = debugfs_create_file("card_insert", S_IRUSR | S_IWUSR, sdhcidir, host, &sdhci_tegra_card_insert_fops); if (!retval) goto err; return; err: debugfs_remove_recursive(sdhcidir); sdhcidir = NULL; dev_err(mmc_dev(host->mmc), "%s %s\n" , __func__, mmc_hostname(host->mmc)); return; #endif } static struct platform_driver sdhci_tegra_driver = { .driver = { .name = "sdhci-tegra", .of_match_table = sdhci_tegra_dt_match, .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_tegra_probe, .remove = sdhci_pltfm_unregister, }; module_platform_driver(sdhci_tegra_driver); module_param(en_boot_part_access, uint, 0444); MODULE_DESCRIPTION("SDHCI driver for Tegra"); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL v2");