162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2010 Google, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/iommu.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/ktime.h> 1762306a36Sopenharmony_ci#include <linux/mmc/card.h> 1862306a36Sopenharmony_ci#include <linux/mmc/host.h> 1962306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 2062306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/pm_opp.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2862306a36Sopenharmony_ci#include <linux/reset.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <soc/tegra/common.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "sdhci-cqhci.h" 3362306a36Sopenharmony_ci#include "sdhci-pltfm.h" 3462306a36Sopenharmony_ci#include "cqhci.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Tegra SDHOST controller vendor register definitions */ 3762306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 3862306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 3962306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 4062306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1f000000 4162306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24 4262306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 4362306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 4462306a36Sopenharmony_ci#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL 0x104 4762306a36Sopenharmony_ci#define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE BIT(31) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES 0x10c 5062306a36Sopenharmony_ci#define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK 0x00003f00 5162306a36Sopenharmony_ci#define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT 8 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 5462306a36Sopenharmony_ci#define SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT BIT(0) 5562306a36Sopenharmony_ci#define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 5662306a36Sopenharmony_ci#define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 5762306a36Sopenharmony_ci#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 5862306a36Sopenharmony_ci#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_DLLCAL_CFG 0x1b0 6162306a36Sopenharmony_ci#define SDHCI_TEGRA_DLLCAL_CALIBRATE BIT(31) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define SDHCI_TEGRA_VENDOR_DLLCAL_STA 0x1bc 6462306a36Sopenharmony_ci#define SDHCI_TEGRA_DLLCAL_STA_ACTIVE BIT(31) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 6762306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 6862306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK 0x03fc0000 6962306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT 18 7062306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK 0x00001fc0 7162306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT 6 7262306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK 0x000e000 7362306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT 13 7462306a36Sopenharmony_ci#define TRIES_128 2 7562306a36Sopenharmony_ci#define TRIES_256 4 7662306a36Sopenharmony_ci#define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK 0x7 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define SDHCI_TEGRA_VNDR_TUN_CTRL1_0 0x1c4 7962306a36Sopenharmony_ci#define SDHCI_TEGRA_VNDR_TUN_STATUS0 0x1C8 8062306a36Sopenharmony_ci#define SDHCI_TEGRA_VNDR_TUN_STATUS1 0x1CC 8162306a36Sopenharmony_ci#define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK 0xFF 8262306a36Sopenharmony_ci#define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT 0x8 8362306a36Sopenharmony_ci#define TUNING_WORD_BIT_SIZE 32 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 8662306a36Sopenharmony_ci#define SDHCI_AUTO_CAL_START BIT(31) 8762306a36Sopenharmony_ci#define SDHCI_AUTO_CAL_ENABLE BIT(29) 8862306a36Sopenharmony_ci#define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0 9162306a36Sopenharmony_ci#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f 9262306a36Sopenharmony_ci#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7 9362306a36Sopenharmony_ci#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31) 9462306a36Sopenharmony_ci#define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK 0x07FFF000 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec 9762306a36Sopenharmony_ci#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define SDHCI_TEGRA_CIF2AXI_CTRL_0 0x1fc 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 10262306a36Sopenharmony_ci#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 10362306a36Sopenharmony_ci#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 10462306a36Sopenharmony_ci#define NVQUIRK_ENABLE_SDR50 BIT(3) 10562306a36Sopenharmony_ci#define NVQUIRK_ENABLE_SDR104 BIT(4) 10662306a36Sopenharmony_ci#define NVQUIRK_ENABLE_DDR50 BIT(5) 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * HAS_PADCALIB NVQUIRK is for SoC's supporting auto calibration of pads 10962306a36Sopenharmony_ci * drive strength. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci#define NVQUIRK_HAS_PADCALIB BIT(6) 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * NEEDS_PAD_CONTROL NVQUIRK is for SoC's having separate 3V3 and 1V8 pads. 11462306a36Sopenharmony_ci * 3V3/1V8 pad selection happens through pinctrl state selection depending 11562306a36Sopenharmony_ci * on the signaling mode. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci#define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) 11862306a36Sopenharmony_ci#define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) 11962306a36Sopenharmony_ci#define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * NVQUIRK_HAS_TMCLK is for SoC's having separate timeout clock for Tegra 12362306a36Sopenharmony_ci * SDMMC hardware data timeout. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci#define NVQUIRK_HAS_TMCLK BIT(10) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11) 12862306a36Sopenharmony_ci#define NVQUIRK_PROGRAM_STREAMID BIT(12) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ 13162306a36Sopenharmony_ci#define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define SDHCI_TEGRA_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ 13462306a36Sopenharmony_ci SDHCI_TRNS_BLK_CNT_EN | \ 13562306a36Sopenharmony_ci SDHCI_TRNS_DMA) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistruct sdhci_tegra_soc_data { 13862306a36Sopenharmony_ci const struct sdhci_pltfm_data *pdata; 13962306a36Sopenharmony_ci u64 dma_mask; 14062306a36Sopenharmony_ci u32 nvquirks; 14162306a36Sopenharmony_ci u8 min_tap_delay; 14262306a36Sopenharmony_ci u8 max_tap_delay; 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* Magic pull up and pull down pad calibration offsets */ 14662306a36Sopenharmony_cistruct sdhci_tegra_autocal_offsets { 14762306a36Sopenharmony_ci u32 pull_up_3v3; 14862306a36Sopenharmony_ci u32 pull_down_3v3; 14962306a36Sopenharmony_ci u32 pull_up_3v3_timeout; 15062306a36Sopenharmony_ci u32 pull_down_3v3_timeout; 15162306a36Sopenharmony_ci u32 pull_up_1v8; 15262306a36Sopenharmony_ci u32 pull_down_1v8; 15362306a36Sopenharmony_ci u32 pull_up_1v8_timeout; 15462306a36Sopenharmony_ci u32 pull_down_1v8_timeout; 15562306a36Sopenharmony_ci u32 pull_up_sdr104; 15662306a36Sopenharmony_ci u32 pull_down_sdr104; 15762306a36Sopenharmony_ci u32 pull_up_hs400; 15862306a36Sopenharmony_ci u32 pull_down_hs400; 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistruct sdhci_tegra { 16262306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data; 16362306a36Sopenharmony_ci struct gpio_desc *power_gpio; 16462306a36Sopenharmony_ci struct clk *tmclk; 16562306a36Sopenharmony_ci bool ddr_signaling; 16662306a36Sopenharmony_ci bool pad_calib_required; 16762306a36Sopenharmony_ci bool pad_control_available; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci struct reset_control *rst; 17062306a36Sopenharmony_ci struct pinctrl *pinctrl_sdmmc; 17162306a36Sopenharmony_ci struct pinctrl_state *pinctrl_state_3v3; 17262306a36Sopenharmony_ci struct pinctrl_state *pinctrl_state_1v8; 17362306a36Sopenharmony_ci struct pinctrl_state *pinctrl_state_3v3_drv; 17462306a36Sopenharmony_ci struct pinctrl_state *pinctrl_state_1v8_drv; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci struct sdhci_tegra_autocal_offsets autocal_offsets; 17762306a36Sopenharmony_ci ktime_t last_calib; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci u32 default_tap; 18062306a36Sopenharmony_ci u32 default_trim; 18162306a36Sopenharmony_ci u32 dqs_trim; 18262306a36Sopenharmony_ci bool enable_hwcq; 18362306a36Sopenharmony_ci unsigned long curr_clk_rate; 18462306a36Sopenharmony_ci u8 tuned_tap_delay; 18562306a36Sopenharmony_ci u32 stream_id; 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 19162306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 19262306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 19562306a36Sopenharmony_ci (reg == SDHCI_HOST_VERSION))) { 19662306a36Sopenharmony_ci /* Erratum: Version register is invalid in HW. */ 19762306a36Sopenharmony_ci return SDHCI_SPEC_200; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return readw(host->ioaddr + reg); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci switch (reg) { 20862306a36Sopenharmony_ci case SDHCI_TRANSFER_MODE: 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Postpone this write, we must do it together with a 21162306a36Sopenharmony_ci * command write that is down below. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci pltfm_host->xfer_mode_shadow = val; 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci case SDHCI_COMMAND: 21662306a36Sopenharmony_ci writel((val << 16) | pltfm_host->xfer_mode_shadow, 21762306a36Sopenharmony_ci host->ioaddr + SDHCI_TRANSFER_MODE); 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci writew(val, host->ioaddr + reg); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 22762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 22862306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Seems like we're getting spurious timeout and crc errors, so 23162306a36Sopenharmony_ci * disable signalling of them. In case of real errors software 23262306a36Sopenharmony_ci * timers should take care of eventually detecting them. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 23562306a36Sopenharmony_ci val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci writel(val, host->ioaddr + reg); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 24062306a36Sopenharmony_ci (reg == SDHCI_INT_ENABLE))) { 24162306a36Sopenharmony_ci /* Erratum: Must enable block gap interrupt detection */ 24262306a36Sopenharmony_ci u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 24362306a36Sopenharmony_ci if (val & SDHCI_INT_CARD_INT) 24462306a36Sopenharmony_ci gap_ctrl |= 0x8; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci gap_ctrl &= ~0x8; 24762306a36Sopenharmony_ci writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci bool status; 25462306a36Sopenharmony_ci u32 reg; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 25762306a36Sopenharmony_ci status = !!(reg & SDHCI_CLOCK_CARD_EN); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (status == enable) 26062306a36Sopenharmony_ci return status; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (enable) 26362306a36Sopenharmony_ci reg |= SDHCI_CLOCK_CARD_EN; 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci reg &= ~SDHCI_CLOCK_CARD_EN; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return status; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci bool is_tuning_cmd = 0; 27562306a36Sopenharmony_ci bool clk_enabled; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (reg == SDHCI_COMMAND) 27862306a36Sopenharmony_ci is_tuning_cmd = mmc_op_tuning(SDHCI_GET_CMD(val)); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (is_tuning_cmd) 28162306a36Sopenharmony_ci clk_enabled = tegra_sdhci_configure_card_clk(host, 0); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci writew(val, host->ioaddr + reg); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (is_tuning_cmd) { 28662306a36Sopenharmony_ci udelay(1); 28762306a36Sopenharmony_ci sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 28862306a36Sopenharmony_ci tegra_sdhci_configure_card_clk(host, clk_enabled); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Write-enable shall be assumed if GPIO is missing in a board's 29662306a36Sopenharmony_ci * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on 29762306a36Sopenharmony_ci * Tegra. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci return mmc_gpio_get_ro(host->mmc); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 30562306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 30662306a36Sopenharmony_ci int has_1v8, has_3v3; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad 31062306a36Sopenharmony_ci * voltage configuration in order to perform voltage switching. This 31162306a36Sopenharmony_ci * means that valid pinctrl info is required on SDHCI instances capable 31262306a36Sopenharmony_ci * of performing voltage switching. Whether or not an SDHCI instance is 31362306a36Sopenharmony_ci * capable of voltage switching is determined based on the regulator. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 31762306a36Sopenharmony_ci return true; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (IS_ERR(host->mmc->supply.vqmmc)) 32062306a36Sopenharmony_ci return false; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 32362306a36Sopenharmony_ci 1700000, 1950000); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 32662306a36Sopenharmony_ci 2700000, 3600000); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (has_1v8 == 1 && has_3v3 == 1) 32962306a36Sopenharmony_ci return tegra_host->pad_control_available; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Fixed voltage, no pad control required. */ 33262306a36Sopenharmony_ci return true; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 33862306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 33962306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 34062306a36Sopenharmony_ci bool card_clk_enabled = false; 34162306a36Sopenharmony_ci u32 reg; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * Touching the tap values is a bit tricky on some SoC generations. 34562306a36Sopenharmony_ci * The quirk enables a workaround for a glitch that sometimes occurs if 34662306a36Sopenharmony_ci * the tap values are changed. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) 35062306a36Sopenharmony_ci card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 35362306a36Sopenharmony_ci reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 35462306a36Sopenharmony_ci reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 35562306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP && 35862306a36Sopenharmony_ci card_clk_enabled) { 35962306a36Sopenharmony_ci udelay(1); 36062306a36Sopenharmony_ci sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 36162306a36Sopenharmony_ci tegra_sdhci_configure_card_clk(host, card_clk_enabled); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 36862306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 36962306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 37062306a36Sopenharmony_ci u32 misc_ctrl, clk_ctrl, pad_ctrl; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci sdhci_and_cqhci_reset(host, mask); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!(mask & SDHCI_RESET_ALL)) 37562306a36Sopenharmony_ci return; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci tegra_sdhci_set_tap(host, tegra_host->default_tap); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 38062306a36Sopenharmony_ci clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | 38362306a36Sopenharmony_ci SDHCI_MISC_CTRL_ENABLE_SDR50 | 38462306a36Sopenharmony_ci SDHCI_MISC_CTRL_ENABLE_DDR50 | 38562306a36Sopenharmony_ci SDHCI_MISC_CTRL_ENABLE_SDR104); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK | 38862306a36Sopenharmony_ci SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (tegra_sdhci_is_pad_and_regulator_valid(host)) { 39162306a36Sopenharmony_ci /* Erratum: Enable SDHCI spec v3.00 support */ 39262306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 39362306a36Sopenharmony_ci misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 39462306a36Sopenharmony_ci /* Advertise UHS modes as supported by host */ 39562306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 39662306a36Sopenharmony_ci misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 39762306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 39862306a36Sopenharmony_ci misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 39962306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 40062306a36Sopenharmony_ci misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 40162306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 40262306a36Sopenharmony_ci clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 40862306a36Sopenharmony_ci sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) { 41162306a36Sopenharmony_ci pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 41262306a36Sopenharmony_ci pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK; 41362306a36Sopenharmony_ci pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL; 41462306a36Sopenharmony_ci sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci tegra_host->pad_calib_required = true; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci tegra_host->ddr_signaling = false; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci u32 val; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * Enable or disable the additional I/O pad used by the drive strength 42862306a36Sopenharmony_ci * calibration process. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (enable) 43362306a36Sopenharmony_ci val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 43462306a36Sopenharmony_ci else 43562306a36Sopenharmony_ci val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (enable) 44062306a36Sopenharmony_ci usleep_range(1, 2); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host, 44462306a36Sopenharmony_ci u16 pdpu) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci u32 reg; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 44962306a36Sopenharmony_ci reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK; 45062306a36Sopenharmony_ci reg |= pdpu; 45162306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage, 45562306a36Sopenharmony_ci bool state_drvupdn) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 45862306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 45962306a36Sopenharmony_ci struct sdhci_tegra_autocal_offsets *offsets = 46062306a36Sopenharmony_ci &tegra_host->autocal_offsets; 46162306a36Sopenharmony_ci struct pinctrl_state *pinctrl_drvupdn = NULL; 46262306a36Sopenharmony_ci int ret = 0; 46362306a36Sopenharmony_ci u8 drvup = 0, drvdn = 0; 46462306a36Sopenharmony_ci u32 reg; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!state_drvupdn) { 46762306a36Sopenharmony_ci /* PADS Drive Strength */ 46862306a36Sopenharmony_ci if (voltage == MMC_SIGNAL_VOLTAGE_180) { 46962306a36Sopenharmony_ci if (tegra_host->pinctrl_state_1v8_drv) { 47062306a36Sopenharmony_ci pinctrl_drvupdn = 47162306a36Sopenharmony_ci tegra_host->pinctrl_state_1v8_drv; 47262306a36Sopenharmony_ci } else { 47362306a36Sopenharmony_ci drvup = offsets->pull_up_1v8_timeout; 47462306a36Sopenharmony_ci drvdn = offsets->pull_down_1v8_timeout; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci if (tegra_host->pinctrl_state_3v3_drv) { 47862306a36Sopenharmony_ci pinctrl_drvupdn = 47962306a36Sopenharmony_ci tegra_host->pinctrl_state_3v3_drv; 48062306a36Sopenharmony_ci } else { 48162306a36Sopenharmony_ci drvup = offsets->pull_up_3v3_timeout; 48262306a36Sopenharmony_ci drvdn = offsets->pull_down_3v3_timeout; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (pinctrl_drvupdn != NULL) { 48762306a36Sopenharmony_ci ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 48862306a36Sopenharmony_ci pinctrl_drvupdn); 48962306a36Sopenharmony_ci if (ret < 0) 49062306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 49162306a36Sopenharmony_ci "failed pads drvupdn, ret: %d\n", ret); 49262306a36Sopenharmony_ci } else if ((drvup) || (drvdn)) { 49362306a36Sopenharmony_ci reg = sdhci_readl(host, 49462306a36Sopenharmony_ci SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 49562306a36Sopenharmony_ci reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK; 49662306a36Sopenharmony_ci reg |= (drvup << 20) | (drvdn << 12); 49762306a36Sopenharmony_ci sdhci_writel(host, reg, 49862306a36Sopenharmony_ci SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci /* Dual Voltage PADS Voltage selection */ 50362306a36Sopenharmony_ci if (!tegra_host->pad_control_available) 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (voltage == MMC_SIGNAL_VOLTAGE_180) { 50762306a36Sopenharmony_ci ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 50862306a36Sopenharmony_ci tegra_host->pinctrl_state_1v8); 50962306a36Sopenharmony_ci if (ret < 0) 51062306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 51162306a36Sopenharmony_ci "setting 1.8V failed, ret: %d\n", ret); 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 51462306a36Sopenharmony_ci tegra_host->pinctrl_state_3v3); 51562306a36Sopenharmony_ci if (ret < 0) 51662306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 51762306a36Sopenharmony_ci "setting 3.3V failed, ret: %d\n", ret); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void tegra_sdhci_pad_autocalib(struct sdhci_host *host) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 52762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 52862306a36Sopenharmony_ci struct sdhci_tegra_autocal_offsets offsets = 52962306a36Sopenharmony_ci tegra_host->autocal_offsets; 53062306a36Sopenharmony_ci struct mmc_ios *ios = &host->mmc->ios; 53162306a36Sopenharmony_ci bool card_clk_enabled; 53262306a36Sopenharmony_ci u16 pdpu; 53362306a36Sopenharmony_ci u32 reg; 53462306a36Sopenharmony_ci int ret; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci switch (ios->timing) { 53762306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 53862306a36Sopenharmony_ci pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104; 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case MMC_TIMING_MMC_HS400: 54162306a36Sopenharmony_ci pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400; 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci default: 54462306a36Sopenharmony_ci if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) 54562306a36Sopenharmony_ci pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8; 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Set initial offset before auto-calibration */ 55162306a36Sopenharmony_ci tegra_sdhci_set_pad_autocal_offset(host, pdpu); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci tegra_sdhci_configure_cal_pad(host, true); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 55862306a36Sopenharmony_ci reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; 55962306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci usleep_range(1, 2); 56262306a36Sopenharmony_ci /* 10 ms timeout */ 56362306a36Sopenharmony_ci ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS, 56462306a36Sopenharmony_ci reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE), 56562306a36Sopenharmony_ci 1000, 10000); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci tegra_sdhci_configure_cal_pad(host, false); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci tegra_sdhci_configure_card_clk(host, card_clk_enabled); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (ret) { 57262306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n"); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Disable automatic cal and use fixed Drive Strengths */ 57562306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 57662306a36Sopenharmony_ci reg &= ~SDHCI_AUTO_CAL_ENABLE; 57762306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false); 58062306a36Sopenharmony_ci if (ret < 0) 58162306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 58262306a36Sopenharmony_ci "Setting drive strengths failed: %d\n", ret); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 58962306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 59062306a36Sopenharmony_ci struct sdhci_tegra_autocal_offsets *autocal = 59162306a36Sopenharmony_ci &tegra_host->autocal_offsets; 59262306a36Sopenharmony_ci int err; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 59562306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-3v3", 59662306a36Sopenharmony_ci &autocal->pull_up_3v3); 59762306a36Sopenharmony_ci if (err) 59862306a36Sopenharmony_ci autocal->pull_up_3v3 = 0; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 60162306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-3v3", 60262306a36Sopenharmony_ci &autocal->pull_down_3v3); 60362306a36Sopenharmony_ci if (err) 60462306a36Sopenharmony_ci autocal->pull_down_3v3 = 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 60762306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-1v8", 60862306a36Sopenharmony_ci &autocal->pull_up_1v8); 60962306a36Sopenharmony_ci if (err) 61062306a36Sopenharmony_ci autocal->pull_up_1v8 = 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 61362306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-1v8", 61462306a36Sopenharmony_ci &autocal->pull_down_1v8); 61562306a36Sopenharmony_ci if (err) 61662306a36Sopenharmony_ci autocal->pull_down_1v8 = 0; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 61962306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-sdr104", 62062306a36Sopenharmony_ci &autocal->pull_up_sdr104); 62162306a36Sopenharmony_ci if (err) 62262306a36Sopenharmony_ci autocal->pull_up_sdr104 = autocal->pull_up_1v8; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 62562306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-sdr104", 62662306a36Sopenharmony_ci &autocal->pull_down_sdr104); 62762306a36Sopenharmony_ci if (err) 62862306a36Sopenharmony_ci autocal->pull_down_sdr104 = autocal->pull_down_1v8; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 63162306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-hs400", 63262306a36Sopenharmony_ci &autocal->pull_up_hs400); 63362306a36Sopenharmony_ci if (err) 63462306a36Sopenharmony_ci autocal->pull_up_hs400 = autocal->pull_up_1v8; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 63762306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-hs400", 63862306a36Sopenharmony_ci &autocal->pull_down_hs400); 63962306a36Sopenharmony_ci if (err) 64062306a36Sopenharmony_ci autocal->pull_down_hs400 = autocal->pull_down_1v8; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * Different fail-safe drive strength values based on the signaling 64462306a36Sopenharmony_ci * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls. 64562306a36Sopenharmony_ci * So, avoid reading below device tree properties for SoCs that don't 64662306a36Sopenharmony_ci * have NVQUIRK_NEEDS_PAD_CONTROL. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 65262306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-3v3-timeout", 65362306a36Sopenharmony_ci &autocal->pull_up_3v3_timeout); 65462306a36Sopenharmony_ci if (err) { 65562306a36Sopenharmony_ci if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 65662306a36Sopenharmony_ci (tegra_host->pinctrl_state_3v3_drv == NULL)) 65762306a36Sopenharmony_ci pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 65862306a36Sopenharmony_ci mmc_hostname(host->mmc)); 65962306a36Sopenharmony_ci autocal->pull_up_3v3_timeout = 0; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 66362306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-3v3-timeout", 66462306a36Sopenharmony_ci &autocal->pull_down_3v3_timeout); 66562306a36Sopenharmony_ci if (err) { 66662306a36Sopenharmony_ci if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 66762306a36Sopenharmony_ci (tegra_host->pinctrl_state_3v3_drv == NULL)) 66862306a36Sopenharmony_ci pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 66962306a36Sopenharmony_ci mmc_hostname(host->mmc)); 67062306a36Sopenharmony_ci autocal->pull_down_3v3_timeout = 0; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 67462306a36Sopenharmony_ci "nvidia,pad-autocal-pull-up-offset-1v8-timeout", 67562306a36Sopenharmony_ci &autocal->pull_up_1v8_timeout); 67662306a36Sopenharmony_ci if (err) { 67762306a36Sopenharmony_ci if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 67862306a36Sopenharmony_ci (tegra_host->pinctrl_state_1v8_drv == NULL)) 67962306a36Sopenharmony_ci pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 68062306a36Sopenharmony_ci mmc_hostname(host->mmc)); 68162306a36Sopenharmony_ci autocal->pull_up_1v8_timeout = 0; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), 68562306a36Sopenharmony_ci "nvidia,pad-autocal-pull-down-offset-1v8-timeout", 68662306a36Sopenharmony_ci &autocal->pull_down_1v8_timeout); 68762306a36Sopenharmony_ci if (err) { 68862306a36Sopenharmony_ci if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 68962306a36Sopenharmony_ci (tegra_host->pinctrl_state_1v8_drv == NULL)) 69062306a36Sopenharmony_ci pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 69162306a36Sopenharmony_ci mmc_hostname(host->mmc)); 69262306a36Sopenharmony_ci autocal->pull_down_1v8_timeout = 0; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 69962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 70062306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 70162306a36Sopenharmony_ci ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* 100 ms calibration interval is specified in the TRM */ 70462306a36Sopenharmony_ci if (ktime_to_ms(since_calib) > 100) { 70562306a36Sopenharmony_ci tegra_sdhci_pad_autocalib(host); 70662306a36Sopenharmony_ci tegra_host->last_calib = ktime_get(); 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci sdhci_request(mmc, mrq); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 71562306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 71662306a36Sopenharmony_ci int err; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-tap", 71962306a36Sopenharmony_ci &tegra_host->default_tap); 72062306a36Sopenharmony_ci if (err) 72162306a36Sopenharmony_ci tegra_host->default_tap = 0; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-trim", 72462306a36Sopenharmony_ci &tegra_host->default_trim); 72562306a36Sopenharmony_ci if (err) 72662306a36Sopenharmony_ci tegra_host->default_trim = 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,dqs-trim", 72962306a36Sopenharmony_ci &tegra_host->dqs_trim); 73062306a36Sopenharmony_ci if (err) 73162306a36Sopenharmony_ci tegra_host->dqs_trim = 0x11; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void tegra_sdhci_parse_dt(struct sdhci_host *host) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 73762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (device_property_read_bool(mmc_dev(host->mmc), "supports-cqe")) 74062306a36Sopenharmony_ci tegra_host->enable_hwcq = true; 74162306a36Sopenharmony_ci else 74262306a36Sopenharmony_ci tegra_host->enable_hwcq = false; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci tegra_sdhci_parse_pad_autocal_dt(host); 74562306a36Sopenharmony_ci tegra_sdhci_parse_tap_and_trim(host); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 75162306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 75262306a36Sopenharmony_ci struct device *dev = mmc_dev(host->mmc); 75362306a36Sopenharmony_ci unsigned long host_clk; 75462306a36Sopenharmony_ci int err; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (!clock) 75762306a36Sopenharmony_ci return sdhci_set_clock(host, clock); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI 76162306a36Sopenharmony_ci * divider to be configured to divided the host clock by two. The SDHCI 76262306a36Sopenharmony_ci * clock divider is calculated as part of sdhci_set_clock() by 76362306a36Sopenharmony_ci * sdhci_calc_clk(). The divider is calculated from host->max_clk and 76462306a36Sopenharmony_ci * the requested clock rate. 76562306a36Sopenharmony_ci * 76662306a36Sopenharmony_ci * By setting the host->max_clk to clock * 2 the divider calculation 76762306a36Sopenharmony_ci * will always result in the correct value for DDR50/52 modes, 76862306a36Sopenharmony_ci * regardless of clock rate rounding, which may happen if the value 76962306a36Sopenharmony_ci * from clk_get_rate() is used. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci err = dev_pm_opp_set_rate(dev, host_clk); 77462306a36Sopenharmony_ci if (err) 77562306a36Sopenharmony_ci dev_err(dev, "failed to set clk rate to %luHz: %d\n", 77662306a36Sopenharmony_ci host_clk, err); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci tegra_host->curr_clk_rate = clk_get_rate(pltfm_host->clk); 77962306a36Sopenharmony_ci if (tegra_host->ddr_signaling) 78062306a36Sopenharmony_ci host->max_clk = host_clk; 78162306a36Sopenharmony_ci else 78262306a36Sopenharmony_ci host->max_clk = clk_get_rate(pltfm_host->clk); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci sdhci_set_clock(host, clock); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (tegra_host->pad_calib_required) { 78762306a36Sopenharmony_ci tegra_sdhci_pad_autocalib(host); 78862306a36Sopenharmony_ci tegra_host->pad_calib_required = false; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, 79362306a36Sopenharmony_ci struct mmc_ios *ios) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 79662306a36Sopenharmony_ci u32 val; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (ios->enhanced_strobe) { 80162306a36Sopenharmony_ci val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * When CMD13 is sent from mmc_select_hs400es() after 80462306a36Sopenharmony_ci * switching to HS400ES mode, the bus is operating at 80562306a36Sopenharmony_ci * either MMC_HIGH_26_MAX_DTR or MMC_HIGH_52_MAX_DTR. 80662306a36Sopenharmony_ci * To meet Tegra SDHCI requirement at HS400ES mode, force SDHCI 80762306a36Sopenharmony_ci * interface clock to MMC_HS200_MAX_DTR (200 MHz) so that host 80862306a36Sopenharmony_ci * controller CAR clock and the interface clock are rate matched. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci tegra_sdhci_set_clock(host, MMC_HS200_MAX_DTR); 81162306a36Sopenharmony_ci } else { 81262306a36Sopenharmony_ci val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return clk_round_rate(pltfm_host->clk, UINT_MAX); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci u32 val; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 83062306a36Sopenharmony_ci val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK; 83162306a36Sopenharmony_ci val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT; 83262306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci u32 reg; 83862306a36Sopenharmony_ci int err; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 84162306a36Sopenharmony_ci reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE; 84262306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 1 ms sleep, 5 ms timeout */ 84562306a36Sopenharmony_ci err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA, 84662306a36Sopenharmony_ci reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE), 84762306a36Sopenharmony_ci 1000, 5000); 84862306a36Sopenharmony_ci if (err) 84962306a36Sopenharmony_ci dev_err(mmc_dev(host->mmc), 85062306a36Sopenharmony_ci "HS400 delay line calibration timed out\n"); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up, 85462306a36Sopenharmony_ci u8 thd_low, u8 fixed_tap) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 85762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 85862306a36Sopenharmony_ci u32 val, tun_status; 85962306a36Sopenharmony_ci u8 word, bit, edge1, tap, window; 86062306a36Sopenharmony_ci bool tap_result; 86162306a36Sopenharmony_ci bool start_fail = false; 86262306a36Sopenharmony_ci bool start_pass = false; 86362306a36Sopenharmony_ci bool end_pass = false; 86462306a36Sopenharmony_ci bool first_fail = false; 86562306a36Sopenharmony_ci bool first_pass = false; 86662306a36Sopenharmony_ci u8 start_pass_tap = 0; 86762306a36Sopenharmony_ci u8 end_pass_tap = 0; 86862306a36Sopenharmony_ci u8 first_fail_tap = 0; 86962306a36Sopenharmony_ci u8 first_pass_tap = 0; 87062306a36Sopenharmony_ci u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * Read auto-tuned results and extract good valid passing window by 87462306a36Sopenharmony_ci * filtering out un-wanted bubble/partial/merged windows. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci for (word = 0; word < total_tuning_words; word++) { 87762306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 87862306a36Sopenharmony_ci val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK; 87962306a36Sopenharmony_ci val |= word; 88062306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 88162306a36Sopenharmony_ci tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0); 88262306a36Sopenharmony_ci bit = 0; 88362306a36Sopenharmony_ci while (bit < TUNING_WORD_BIT_SIZE) { 88462306a36Sopenharmony_ci tap = word * TUNING_WORD_BIT_SIZE + bit; 88562306a36Sopenharmony_ci tap_result = tun_status & (1 << bit); 88662306a36Sopenharmony_ci if (!tap_result && !start_fail) { 88762306a36Sopenharmony_ci start_fail = true; 88862306a36Sopenharmony_ci if (!first_fail) { 88962306a36Sopenharmony_ci first_fail_tap = tap; 89062306a36Sopenharmony_ci first_fail = true; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci } else if (tap_result && start_fail && !start_pass) { 89462306a36Sopenharmony_ci start_pass_tap = tap; 89562306a36Sopenharmony_ci start_pass = true; 89662306a36Sopenharmony_ci if (!first_pass) { 89762306a36Sopenharmony_ci first_pass_tap = tap; 89862306a36Sopenharmony_ci first_pass = true; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci } else if (!tap_result && start_fail && start_pass && 90262306a36Sopenharmony_ci !end_pass) { 90362306a36Sopenharmony_ci end_pass_tap = tap - 1; 90462306a36Sopenharmony_ci end_pass = true; 90562306a36Sopenharmony_ci } else if (tap_result && start_pass && start_fail && 90662306a36Sopenharmony_ci end_pass) { 90762306a36Sopenharmony_ci window = end_pass_tap - start_pass_tap; 90862306a36Sopenharmony_ci /* discard merged window and bubble window */ 90962306a36Sopenharmony_ci if (window >= thd_up || window < thd_low) { 91062306a36Sopenharmony_ci start_pass_tap = tap; 91162306a36Sopenharmony_ci end_pass = false; 91262306a36Sopenharmony_ci } else { 91362306a36Sopenharmony_ci /* set tap at middle of valid window */ 91462306a36Sopenharmony_ci tap = start_pass_tap + window / 2; 91562306a36Sopenharmony_ci tegra_host->tuned_tap_delay = tap; 91662306a36Sopenharmony_ci return; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci bit++; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!first_fail) { 92562306a36Sopenharmony_ci WARN(1, "no edge detected, continue with hw tuned delay.\n"); 92662306a36Sopenharmony_ci } else if (first_pass) { 92762306a36Sopenharmony_ci /* set tap location at fixed tap relative to the first edge */ 92862306a36Sopenharmony_ci edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2; 92962306a36Sopenharmony_ci if (edge1 - 1 > fixed_tap) 93062306a36Sopenharmony_ci tegra_host->tuned_tap_delay = edge1 - fixed_tap; 93162306a36Sopenharmony_ci else 93262306a36Sopenharmony_ci tegra_host->tuned_tap_delay = edge1 + fixed_tap; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic void tegra_sdhci_post_tuning(struct sdhci_host *host) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 93962306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 94062306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 94162306a36Sopenharmony_ci u32 avg_tap_dly, val, min_tap_dly, max_tap_dly; 94262306a36Sopenharmony_ci u8 fixed_tap, start_tap, end_tap, window_width; 94362306a36Sopenharmony_ci u8 thdupper, thdlower; 94462306a36Sopenharmony_ci u8 num_iter; 94562306a36Sopenharmony_ci u32 clk_rate_mhz, period_ps, bestcase, worstcase; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* retain HW tuned tap to use incase if no correction is needed */ 94862306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 94962306a36Sopenharmony_ci tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >> 95062306a36Sopenharmony_ci SDHCI_CLOCK_CTRL_TAP_SHIFT; 95162306a36Sopenharmony_ci if (soc_data->min_tap_delay && soc_data->max_tap_delay) { 95262306a36Sopenharmony_ci min_tap_dly = soc_data->min_tap_delay; 95362306a36Sopenharmony_ci max_tap_dly = soc_data->max_tap_delay; 95462306a36Sopenharmony_ci clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC; 95562306a36Sopenharmony_ci period_ps = USEC_PER_SEC / clk_rate_mhz; 95662306a36Sopenharmony_ci bestcase = period_ps / min_tap_dly; 95762306a36Sopenharmony_ci worstcase = period_ps / max_tap_dly; 95862306a36Sopenharmony_ci /* 95962306a36Sopenharmony_ci * Upper and Lower bound thresholds used to detect merged and 96062306a36Sopenharmony_ci * bubble windows 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_ci thdupper = (2 * worstcase + bestcase) / 2; 96362306a36Sopenharmony_ci thdlower = worstcase / 4; 96462306a36Sopenharmony_ci /* 96562306a36Sopenharmony_ci * fixed tap is used when HW tuning result contains single edge 96662306a36Sopenharmony_ci * and tap is set at fixed tap delay relative to the first edge 96762306a36Sopenharmony_ci */ 96862306a36Sopenharmony_ci avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly); 96962306a36Sopenharmony_ci fixed_tap = avg_tap_dly / 2; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1); 97262306a36Sopenharmony_ci start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 97362306a36Sopenharmony_ci end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) & 97462306a36Sopenharmony_ci SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 97562306a36Sopenharmony_ci window_width = end_tap - start_tap; 97662306a36Sopenharmony_ci num_iter = host->tuning_loop_count; 97762306a36Sopenharmony_ci /* 97862306a36Sopenharmony_ci * partial window includes edges of the tuning range. 97962306a36Sopenharmony_ci * merged window includes more taps so window width is higher 98062306a36Sopenharmony_ci * than upper threshold. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci if (start_tap == 0 || (end_tap == (num_iter - 1)) || 98362306a36Sopenharmony_ci (end_tap == num_iter - 2) || window_width >= thdupper) { 98462306a36Sopenharmony_ci pr_debug("%s: Apply tuning correction\n", 98562306a36Sopenharmony_ci mmc_hostname(host->mmc)); 98662306a36Sopenharmony_ci tegra_sdhci_tap_correction(host, thdupper, thdlower, 98762306a36Sopenharmony_ci fixed_tap); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 99762306a36Sopenharmony_ci int err; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci err = sdhci_execute_tuning(mmc, opcode); 100062306a36Sopenharmony_ci if (!err && !host->tuning_err) 100162306a36Sopenharmony_ci tegra_sdhci_post_tuning(host); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return err; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 100762306a36Sopenharmony_ci unsigned timing) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 101062306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 101162306a36Sopenharmony_ci bool set_default_tap = false; 101262306a36Sopenharmony_ci bool set_dqs_trim = false; 101362306a36Sopenharmony_ci bool do_hs400_dll_cal = false; 101462306a36Sopenharmony_ci u8 iter = TRIES_256; 101562306a36Sopenharmony_ci u32 val; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci tegra_host->ddr_signaling = false; 101862306a36Sopenharmony_ci switch (timing) { 101962306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 102262306a36Sopenharmony_ci case MMC_TIMING_MMC_HS200: 102362306a36Sopenharmony_ci /* Don't set default tap on tunable modes. */ 102462306a36Sopenharmony_ci iter = TRIES_128; 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case MMC_TIMING_MMC_HS400: 102762306a36Sopenharmony_ci set_dqs_trim = true; 102862306a36Sopenharmony_ci do_hs400_dll_cal = true; 102962306a36Sopenharmony_ci iter = TRIES_128; 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 103262306a36Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 103362306a36Sopenharmony_ci tegra_host->ddr_signaling = true; 103462306a36Sopenharmony_ci set_default_tap = true; 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci set_default_tap = true; 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 104262306a36Sopenharmony_ci val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK | 104362306a36Sopenharmony_ci SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK | 104462306a36Sopenharmony_ci SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK); 104562306a36Sopenharmony_ci val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT | 104662306a36Sopenharmony_ci 0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT | 104762306a36Sopenharmony_ci 1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT); 104862306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 104962306a36Sopenharmony_ci sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci sdhci_set_uhs_signaling(host, timing); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci tegra_sdhci_pad_autocalib(host); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (tegra_host->tuned_tap_delay && !set_default_tap) 105862306a36Sopenharmony_ci tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 105962306a36Sopenharmony_ci else 106062306a36Sopenharmony_ci tegra_sdhci_set_tap(host, tegra_host->default_tap); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (set_dqs_trim) 106362306a36Sopenharmony_ci tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (do_hs400_dll_cal) 106662306a36Sopenharmony_ci tegra_sdhci_hs400_dll_cal(host); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci unsigned int min, max; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * Start search for minimum tap value at 10, as smaller values are 107562306a36Sopenharmony_ci * may wrongly be reported as working but fail at higher speeds, 107662306a36Sopenharmony_ci * according to the TRM. 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci min = 10; 107962306a36Sopenharmony_ci while (min < 255) { 108062306a36Sopenharmony_ci tegra_sdhci_set_tap(host, min); 108162306a36Sopenharmony_ci if (!mmc_send_tuning(host->mmc, opcode, NULL)) 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci min++; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* Find the maximum tap value that still passes. */ 108762306a36Sopenharmony_ci max = min + 1; 108862306a36Sopenharmony_ci while (max < 255) { 108962306a36Sopenharmony_ci tegra_sdhci_set_tap(host, max); 109062306a36Sopenharmony_ci if (mmc_send_tuning(host->mmc, opcode, NULL)) { 109162306a36Sopenharmony_ci max--; 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci max++; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* The TRM states the ideal tap value is at 75% in the passing range. */ 109862306a36Sopenharmony_ci tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return mmc_send_tuning(host->mmc, opcode, NULL); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc, 110462306a36Sopenharmony_ci struct mmc_ios *ios) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 110762306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 110862306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 110962306a36Sopenharmony_ci int ret = 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { 111262306a36Sopenharmony_ci ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 111362306a36Sopenharmony_ci if (ret < 0) 111462306a36Sopenharmony_ci return ret; 111562306a36Sopenharmony_ci ret = sdhci_start_signal_voltage_switch(mmc, ios); 111662306a36Sopenharmony_ci } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { 111762306a36Sopenharmony_ci ret = sdhci_start_signal_voltage_switch(mmc, ios); 111862306a36Sopenharmony_ci if (ret < 0) 111962306a36Sopenharmony_ci return ret; 112062306a36Sopenharmony_ci ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (tegra_host->pad_calib_required) 112462306a36Sopenharmony_ci tegra_sdhci_pad_autocalib(host); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return ret; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int tegra_sdhci_init_pinctrl_info(struct device *dev, 113062306a36Sopenharmony_ci struct sdhci_tegra *tegra_host) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev); 113362306a36Sopenharmony_ci if (IS_ERR(tegra_host->pinctrl_sdmmc)) { 113462306a36Sopenharmony_ci dev_dbg(dev, "No pinctrl info, err: %ld\n", 113562306a36Sopenharmony_ci PTR_ERR(tegra_host->pinctrl_sdmmc)); 113662306a36Sopenharmony_ci return -1; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state( 114062306a36Sopenharmony_ci tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv"); 114162306a36Sopenharmony_ci if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) { 114262306a36Sopenharmony_ci if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV) 114362306a36Sopenharmony_ci tegra_host->pinctrl_state_1v8_drv = NULL; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state( 114762306a36Sopenharmony_ci tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv"); 114862306a36Sopenharmony_ci if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) { 114962306a36Sopenharmony_ci if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV) 115062306a36Sopenharmony_ci tegra_host->pinctrl_state_3v3_drv = NULL; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci tegra_host->pinctrl_state_3v3 = 115462306a36Sopenharmony_ci pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3"); 115562306a36Sopenharmony_ci if (IS_ERR(tegra_host->pinctrl_state_3v3)) { 115662306a36Sopenharmony_ci dev_warn(dev, "Missing 3.3V pad state, err: %ld\n", 115762306a36Sopenharmony_ci PTR_ERR(tegra_host->pinctrl_state_3v3)); 115862306a36Sopenharmony_ci return -1; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci tegra_host->pinctrl_state_1v8 = 116262306a36Sopenharmony_ci pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8"); 116362306a36Sopenharmony_ci if (IS_ERR(tegra_host->pinctrl_state_1v8)) { 116462306a36Sopenharmony_ci dev_warn(dev, "Missing 1.8V pad state, err: %ld\n", 116562306a36Sopenharmony_ci PTR_ERR(tegra_host->pinctrl_state_1v8)); 116662306a36Sopenharmony_ci return -1; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci tegra_host->pad_control_available = true; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci return 0; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic void tegra_sdhci_voltage_switch(struct sdhci_host *host) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 117762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 117862306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 118162306a36Sopenharmony_ci tegra_host->pad_calib_required = true; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct mmc_host *mmc = cq_host->mmc; 118762306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 118862306a36Sopenharmony_ci u8 ctrl; 118962306a36Sopenharmony_ci ktime_t timeout; 119062306a36Sopenharmony_ci bool timed_out; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* 119362306a36Sopenharmony_ci * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to 119462306a36Sopenharmony_ci * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need 119562306a36Sopenharmony_ci * to be re-configured. 119662306a36Sopenharmony_ci * Tegra CQHCI/SDHCI prevents write access to block size register when 119762306a36Sopenharmony_ci * CQE is unhalted. So handling CQE resume sequence here to configure 119862306a36Sopenharmony_ci * SDHCI block registers prior to exiting CQE halt state. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (reg == CQHCI_CTL && !(val & CQHCI_HALT) && 120162306a36Sopenharmony_ci cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) { 120262306a36Sopenharmony_ci sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); 120362306a36Sopenharmony_ci sdhci_cqe_enable(mmc); 120462306a36Sopenharmony_ci writel(val, cq_host->mmio + reg); 120562306a36Sopenharmony_ci timeout = ktime_add_us(ktime_get(), 50); 120662306a36Sopenharmony_ci while (1) { 120762306a36Sopenharmony_ci timed_out = ktime_compare(ktime_get(), timeout) > 0; 120862306a36Sopenharmony_ci ctrl = cqhci_readl(cq_host, CQHCI_CTL); 120962306a36Sopenharmony_ci if (!(ctrl & CQHCI_HALT) || timed_out) 121062306a36Sopenharmony_ci break; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * CQE usually resumes very quick, but incase if Tegra CQE 121462306a36Sopenharmony_ci * doesn't resume retry unhalt. 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci if (timed_out) 121762306a36Sopenharmony_ci writel(val, cq_host->mmio + reg); 121862306a36Sopenharmony_ci } else { 121962306a36Sopenharmony_ci writel(val, cq_host->mmio + reg); 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc, 122462306a36Sopenharmony_ci struct mmc_request *mrq, u64 *data) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc)); 122762306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 122862306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING && 123162306a36Sopenharmony_ci mrq->cmd->flags & MMC_RSP_R1B) 123262306a36Sopenharmony_ci *data |= CQHCI_CMD_TIMING(1); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic void sdhci_tegra_cqe_enable(struct mmc_host *mmc) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct cqhci_host *cq_host = mmc->cqe_private; 123862306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 123962306a36Sopenharmony_ci u32 val; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci /* 124262306a36Sopenharmony_ci * Tegra CQHCI/SDMMC design prevents write access to sdhci block size 124362306a36Sopenharmony_ci * register when CQE is enabled and unhalted. 124462306a36Sopenharmony_ci * CQHCI driver enables CQE prior to activation, so disable CQE before 124562306a36Sopenharmony_ci * programming block size in sdhci controller and enable it back. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci if (!cq_host->activated) { 124862306a36Sopenharmony_ci val = cqhci_readl(cq_host, CQHCI_CFG); 124962306a36Sopenharmony_ci if (val & CQHCI_ENABLE) 125062306a36Sopenharmony_ci cqhci_writel(cq_host, (val & ~CQHCI_ENABLE), 125162306a36Sopenharmony_ci CQHCI_CFG); 125262306a36Sopenharmony_ci sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); 125362306a36Sopenharmony_ci sdhci_cqe_enable(mmc); 125462306a36Sopenharmony_ci if (val & CQHCI_ENABLE) 125562306a36Sopenharmony_ci cqhci_writel(cq_host, val, CQHCI_CFG); 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* 125962306a36Sopenharmony_ci * CMD CRC errors are seen sometimes with some eMMC devices when status 126062306a36Sopenharmony_ci * command is sent during transfer of last data block which is the 126162306a36Sopenharmony_ci * default case as send status command block counter (CBC) is 1. 126262306a36Sopenharmony_ci * Recommended fix to set CBC to 0 allowing send status command only 126362306a36Sopenharmony_ci * when data lines are idle. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci val = cqhci_readl(cq_host, CQHCI_SSC1); 126662306a36Sopenharmony_ci val &= ~CQHCI_SSC1_CBC_MASK; 126762306a36Sopenharmony_ci cqhci_writel(cq_host, val, CQHCI_SSC1); 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic void sdhci_tegra_dumpregs(struct mmc_host *mmc) 127162306a36Sopenharmony_ci{ 127262306a36Sopenharmony_ci sdhci_dumpregs(mmc_priv(mmc)); 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci int cmd_error = 0; 127862306a36Sopenharmony_ci int data_error = 0; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 128162306a36Sopenharmony_ci return intmask; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci cqhci_irq(host->mmc, intmask, cmd_error, data_error); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return 0; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic void tegra_sdhci_set_timeout(struct sdhci_host *host, 128962306a36Sopenharmony_ci struct mmc_command *cmd) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci u32 val; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* 129462306a36Sopenharmony_ci * HW busy detection timeout is based on programmed data timeout 129562306a36Sopenharmony_ci * counter and maximum supported timeout is 11s which may not be 129662306a36Sopenharmony_ci * enough for long operations like cache flush, sleep awake, erase. 129762306a36Sopenharmony_ci * 129862306a36Sopenharmony_ci * ERASE_TIMEOUT_LIMIT bit of VENDOR_MISC_CTRL register allows 129962306a36Sopenharmony_ci * host controller to wait for busy state until the card is busy 130062306a36Sopenharmony_ci * without HW timeout. 130162306a36Sopenharmony_ci * 130262306a36Sopenharmony_ci * So, use infinite busy wait mode for operations that may take 130362306a36Sopenharmony_ci * more than maximum HW busy timeout of 11s otherwise use finite 130462306a36Sopenharmony_ci * busy wait mode. 130562306a36Sopenharmony_ci */ 130662306a36Sopenharmony_ci val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 130762306a36Sopenharmony_ci if (cmd && cmd->busy_timeout >= 11 * MSEC_PER_SEC) 130862306a36Sopenharmony_ci val |= SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; 130962306a36Sopenharmony_ci else 131062306a36Sopenharmony_ci val &= ~SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; 131162306a36Sopenharmony_ci sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_MISC_CTRL); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci __sdhci_set_timeout(host, cmd); 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic void sdhci_tegra_cqe_pre_enable(struct mmc_host *mmc) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct cqhci_host *cq_host = mmc->cqe_private; 131962306a36Sopenharmony_ci u32 reg; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci reg = cqhci_readl(cq_host, CQHCI_CFG); 132262306a36Sopenharmony_ci reg |= CQHCI_ENABLE; 132362306a36Sopenharmony_ci cqhci_writel(cq_host, reg, CQHCI_CFG); 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic void sdhci_tegra_cqe_post_disable(struct mmc_host *mmc) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci struct cqhci_host *cq_host = mmc->cqe_private; 132962306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 133062306a36Sopenharmony_ci u32 reg; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci reg = cqhci_readl(cq_host, CQHCI_CFG); 133362306a36Sopenharmony_ci reg &= ~CQHCI_ENABLE; 133462306a36Sopenharmony_ci cqhci_writel(cq_host, reg, CQHCI_CFG); 133562306a36Sopenharmony_ci sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { 133962306a36Sopenharmony_ci .write_l = tegra_cqhci_writel, 134062306a36Sopenharmony_ci .enable = sdhci_tegra_cqe_enable, 134162306a36Sopenharmony_ci .disable = sdhci_cqe_disable, 134262306a36Sopenharmony_ci .dumpregs = sdhci_tegra_dumpregs, 134362306a36Sopenharmony_ci .update_dcmd_desc = sdhci_tegra_update_dcmd_desc, 134462306a36Sopenharmony_ci .pre_enable = sdhci_tegra_cqe_pre_enable, 134562306a36Sopenharmony_ci .post_disable = sdhci_tegra_cqe_post_disable, 134662306a36Sopenharmony_ci}; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic int tegra_sdhci_set_dma_mask(struct sdhci_host *host) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci struct sdhci_pltfm_host *platform = sdhci_priv(host); 135162306a36Sopenharmony_ci struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform); 135262306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc = tegra->soc_data; 135362306a36Sopenharmony_ci struct device *dev = mmc_dev(host->mmc); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (soc->dma_mask) 135662306a36Sopenharmony_ci return dma_set_mask_and_coherent(dev, soc->dma_mask); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return 0; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic const struct sdhci_ops tegra_sdhci_ops = { 136262306a36Sopenharmony_ci .get_ro = tegra_sdhci_get_ro, 136362306a36Sopenharmony_ci .read_w = tegra_sdhci_readw, 136462306a36Sopenharmony_ci .write_l = tegra_sdhci_writel, 136562306a36Sopenharmony_ci .set_clock = tegra_sdhci_set_clock, 136662306a36Sopenharmony_ci .set_dma_mask = tegra_sdhci_set_dma_mask, 136762306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 136862306a36Sopenharmony_ci .reset = tegra_sdhci_reset, 136962306a36Sopenharmony_ci .platform_execute_tuning = tegra_sdhci_execute_tuning, 137062306a36Sopenharmony_ci .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 137162306a36Sopenharmony_ci .voltage_switch = tegra_sdhci_voltage_switch, 137262306a36Sopenharmony_ci .get_max_clock = tegra_sdhci_get_max_clock, 137362306a36Sopenharmony_ci}; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 137662306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 137762306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 137862306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 137962306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 138062306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 138162306a36Sopenharmony_ci .ops = &tegra_sdhci_ops, 138262306a36Sopenharmony_ci}; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra20 = { 138562306a36Sopenharmony_ci .pdata = &sdhci_tegra20_pdata, 138662306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 138762306a36Sopenharmony_ci .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 138862306a36Sopenharmony_ci NVQUIRK_HAS_ANDROID_GPT_SECTOR | 138962306a36Sopenharmony_ci NVQUIRK_ENABLE_BLOCK_GAP_DET, 139062306a36Sopenharmony_ci}; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 139362306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 139462306a36Sopenharmony_ci SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 139562306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 139662306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 139762306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 139862306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 139962306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 140062306a36Sopenharmony_ci SDHCI_QUIRK2_BROKEN_HS200 | 140162306a36Sopenharmony_ci /* 140262306a36Sopenharmony_ci * Auto-CMD23 leads to "Got command interrupt 0x00010000 even 140362306a36Sopenharmony_ci * though no command operation was in progress." 140462306a36Sopenharmony_ci * 140562306a36Sopenharmony_ci * The exact reason is unknown, as the same hardware seems 140662306a36Sopenharmony_ci * to support Auto CMD23 on a downstream 3.1 kernel. 140762306a36Sopenharmony_ci */ 140862306a36Sopenharmony_ci SDHCI_QUIRK2_ACMD23_BROKEN, 140962306a36Sopenharmony_ci .ops = &tegra_sdhci_ops, 141062306a36Sopenharmony_ci}; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra30 = { 141362306a36Sopenharmony_ci .pdata = &sdhci_tegra30_pdata, 141462306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 141562306a36Sopenharmony_ci .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 141662306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR50 | 141762306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR104 | 141862306a36Sopenharmony_ci NVQUIRK_HAS_ANDROID_GPT_SECTOR | 141962306a36Sopenharmony_ci NVQUIRK_HAS_PADCALIB, 142062306a36Sopenharmony_ci}; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic const struct sdhci_ops tegra114_sdhci_ops = { 142362306a36Sopenharmony_ci .get_ro = tegra_sdhci_get_ro, 142462306a36Sopenharmony_ci .read_w = tegra_sdhci_readw, 142562306a36Sopenharmony_ci .write_w = tegra_sdhci_writew, 142662306a36Sopenharmony_ci .write_l = tegra_sdhci_writel, 142762306a36Sopenharmony_ci .set_clock = tegra_sdhci_set_clock, 142862306a36Sopenharmony_ci .set_dma_mask = tegra_sdhci_set_dma_mask, 142962306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 143062306a36Sopenharmony_ci .reset = tegra_sdhci_reset, 143162306a36Sopenharmony_ci .platform_execute_tuning = tegra_sdhci_execute_tuning, 143262306a36Sopenharmony_ci .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 143362306a36Sopenharmony_ci .voltage_switch = tegra_sdhci_voltage_switch, 143462306a36Sopenharmony_ci .get_max_clock = tegra_sdhci_get_max_clock, 143562306a36Sopenharmony_ci}; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 143862306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 143962306a36Sopenharmony_ci SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 144062306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 144162306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 144262306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 144362306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 144462306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 144562306a36Sopenharmony_ci .ops = &tegra114_sdhci_ops, 144662306a36Sopenharmony_ci}; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra114 = { 144962306a36Sopenharmony_ci .pdata = &sdhci_tegra114_pdata, 145062306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 145162306a36Sopenharmony_ci .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR, 145262306a36Sopenharmony_ci}; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra124_pdata = { 145562306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 145662306a36Sopenharmony_ci SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 145762306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 145862306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 145962306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 146062306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 146162306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 146262306a36Sopenharmony_ci .ops = &tegra114_sdhci_ops, 146362306a36Sopenharmony_ci}; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra124 = { 146662306a36Sopenharmony_ci .pdata = &sdhci_tegra124_pdata, 146762306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(34), 146862306a36Sopenharmony_ci .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR, 146962306a36Sopenharmony_ci}; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_cistatic const struct sdhci_ops tegra210_sdhci_ops = { 147262306a36Sopenharmony_ci .get_ro = tegra_sdhci_get_ro, 147362306a36Sopenharmony_ci .read_w = tegra_sdhci_readw, 147462306a36Sopenharmony_ci .write_w = tegra210_sdhci_writew, 147562306a36Sopenharmony_ci .write_l = tegra_sdhci_writel, 147662306a36Sopenharmony_ci .set_clock = tegra_sdhci_set_clock, 147762306a36Sopenharmony_ci .set_dma_mask = tegra_sdhci_set_dma_mask, 147862306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 147962306a36Sopenharmony_ci .reset = tegra_sdhci_reset, 148062306a36Sopenharmony_ci .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 148162306a36Sopenharmony_ci .voltage_switch = tegra_sdhci_voltage_switch, 148262306a36Sopenharmony_ci .get_max_clock = tegra_sdhci_get_max_clock, 148362306a36Sopenharmony_ci .set_timeout = tegra_sdhci_set_timeout, 148462306a36Sopenharmony_ci}; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 148762306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 148862306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 148962306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 149062306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 149162306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 149262306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 149362306a36Sopenharmony_ci .ops = &tegra210_sdhci_ops, 149462306a36Sopenharmony_ci}; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra210 = { 149762306a36Sopenharmony_ci .pdata = &sdhci_tegra210_pdata, 149862306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(34), 149962306a36Sopenharmony_ci .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 150062306a36Sopenharmony_ci NVQUIRK_HAS_PADCALIB | 150162306a36Sopenharmony_ci NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 150262306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR50 | 150362306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR104 | 150462306a36Sopenharmony_ci NVQUIRK_HAS_TMCLK, 150562306a36Sopenharmony_ci .min_tap_delay = 106, 150662306a36Sopenharmony_ci .max_tap_delay = 185, 150762306a36Sopenharmony_ci}; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic const struct sdhci_ops tegra186_sdhci_ops = { 151062306a36Sopenharmony_ci .get_ro = tegra_sdhci_get_ro, 151162306a36Sopenharmony_ci .read_w = tegra_sdhci_readw, 151262306a36Sopenharmony_ci .write_l = tegra_sdhci_writel, 151362306a36Sopenharmony_ci .set_clock = tegra_sdhci_set_clock, 151462306a36Sopenharmony_ci .set_dma_mask = tegra_sdhci_set_dma_mask, 151562306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 151662306a36Sopenharmony_ci .reset = tegra_sdhci_reset, 151762306a36Sopenharmony_ci .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 151862306a36Sopenharmony_ci .voltage_switch = tegra_sdhci_voltage_switch, 151962306a36Sopenharmony_ci .get_max_clock = tegra_sdhci_get_max_clock, 152062306a36Sopenharmony_ci .irq = sdhci_tegra_cqhci_irq, 152162306a36Sopenharmony_ci .set_timeout = tegra_sdhci_set_timeout, 152262306a36Sopenharmony_ci}; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_tegra186_pdata = { 152562306a36Sopenharmony_ci .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 152662306a36Sopenharmony_ci SDHCI_QUIRK_SINGLE_POWER_WRITE | 152762306a36Sopenharmony_ci SDHCI_QUIRK_NO_HISPD_BIT | 152862306a36Sopenharmony_ci SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 152962306a36Sopenharmony_ci SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 153062306a36Sopenharmony_ci .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 153162306a36Sopenharmony_ci SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER, 153262306a36Sopenharmony_ci .ops = &tegra186_sdhci_ops, 153362306a36Sopenharmony_ci}; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra186 = { 153662306a36Sopenharmony_ci .pdata = &sdhci_tegra186_pdata, 153762306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(40), 153862306a36Sopenharmony_ci .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 153962306a36Sopenharmony_ci NVQUIRK_HAS_PADCALIB | 154062306a36Sopenharmony_ci NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 154162306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR50 | 154262306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR104 | 154362306a36Sopenharmony_ci NVQUIRK_HAS_TMCLK | 154462306a36Sopenharmony_ci NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING, 154562306a36Sopenharmony_ci .min_tap_delay = 84, 154662306a36Sopenharmony_ci .max_tap_delay = 136, 154762306a36Sopenharmony_ci}; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra194 = { 155062306a36Sopenharmony_ci .pdata = &sdhci_tegra186_pdata, 155162306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(39), 155262306a36Sopenharmony_ci .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 155362306a36Sopenharmony_ci NVQUIRK_HAS_PADCALIB | 155462306a36Sopenharmony_ci NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 155562306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR50 | 155662306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR104 | 155762306a36Sopenharmony_ci NVQUIRK_HAS_TMCLK, 155862306a36Sopenharmony_ci .min_tap_delay = 96, 155962306a36Sopenharmony_ci .max_tap_delay = 139, 156062306a36Sopenharmony_ci}; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic const struct sdhci_tegra_soc_data soc_data_tegra234 = { 156362306a36Sopenharmony_ci .pdata = &sdhci_tegra186_pdata, 156462306a36Sopenharmony_ci .dma_mask = DMA_BIT_MASK(39), 156562306a36Sopenharmony_ci .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 156662306a36Sopenharmony_ci NVQUIRK_HAS_PADCALIB | 156762306a36Sopenharmony_ci NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 156862306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR50 | 156962306a36Sopenharmony_ci NVQUIRK_ENABLE_SDR104 | 157062306a36Sopenharmony_ci NVQUIRK_PROGRAM_STREAMID | 157162306a36Sopenharmony_ci NVQUIRK_HAS_TMCLK, 157262306a36Sopenharmony_ci .min_tap_delay = 95, 157362306a36Sopenharmony_ci .max_tap_delay = 111, 157462306a36Sopenharmony_ci}; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_cistatic const struct of_device_id sdhci_tegra_dt_match[] = { 157762306a36Sopenharmony_ci { .compatible = "nvidia,tegra234-sdhci", .data = &soc_data_tegra234 }, 157862306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 }, 157962306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, 158062306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 158162306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, 158262306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 158362306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 158462306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 158562306a36Sopenharmony_ci {} 158662306a36Sopenharmony_ci}; 158762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic int sdhci_tegra_add_host(struct sdhci_host *host) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 159262306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 159362306a36Sopenharmony_ci struct cqhci_host *cq_host; 159462306a36Sopenharmony_ci bool dma64; 159562306a36Sopenharmony_ci int ret; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!tegra_host->enable_hwcq) 159862306a36Sopenharmony_ci return sdhci_add_host(host); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci sdhci_enable_v4_mode(host); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci ret = sdhci_setup_host(host); 160362306a36Sopenharmony_ci if (ret) 160462306a36Sopenharmony_ci return ret; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci cq_host = devm_kzalloc(mmc_dev(host->mmc), 160962306a36Sopenharmony_ci sizeof(*cq_host), GFP_KERNEL); 161062306a36Sopenharmony_ci if (!cq_host) { 161162306a36Sopenharmony_ci ret = -ENOMEM; 161262306a36Sopenharmony_ci goto cleanup; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR; 161662306a36Sopenharmony_ci cq_host->ops = &sdhci_tegra_cqhci_ops; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 161962306a36Sopenharmony_ci if (dma64) 162062306a36Sopenharmony_ci cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci ret = cqhci_init(cq_host, host->mmc, dma64); 162362306a36Sopenharmony_ci if (ret) 162462306a36Sopenharmony_ci goto cleanup; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ret = __sdhci_add_host(host); 162762306a36Sopenharmony_ci if (ret) 162862306a36Sopenharmony_ci goto cleanup; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci return 0; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_cicleanup: 163362306a36Sopenharmony_ci sdhci_cleanup_host(host); 163462306a36Sopenharmony_ci return ret; 163562306a36Sopenharmony_ci} 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci/* Program MC streamID for DMA transfers */ 163862306a36Sopenharmony_cistatic void sdhci_tegra_program_stream_id(struct sdhci_host *host) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 164162306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID) { 164462306a36Sopenharmony_ci tegra_sdhci_writel(host, FIELD_PREP(GENMASK(15, 8), tegra_host->stream_id) | 164562306a36Sopenharmony_ci FIELD_PREP(GENMASK(7, 0), tegra_host->stream_id), 164662306a36Sopenharmony_ci SDHCI_TEGRA_CIF2AXI_CTRL_0); 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci} 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cistatic int sdhci_tegra_probe(struct platform_device *pdev) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci const struct sdhci_tegra_soc_data *soc_data; 165362306a36Sopenharmony_ci struct sdhci_host *host; 165462306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 165562306a36Sopenharmony_ci struct sdhci_tegra *tegra_host; 165662306a36Sopenharmony_ci struct clk *clk; 165762306a36Sopenharmony_ci int rc; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci soc_data = of_device_get_match_data(&pdev->dev); 166062306a36Sopenharmony_ci if (!soc_data) 166162306a36Sopenharmony_ci return -EINVAL; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 166462306a36Sopenharmony_ci if (IS_ERR(host)) 166562306a36Sopenharmony_ci return PTR_ERR(host); 166662306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci tegra_host = sdhci_pltfm_priv(pltfm_host); 166962306a36Sopenharmony_ci tegra_host->ddr_signaling = false; 167062306a36Sopenharmony_ci tegra_host->pad_calib_required = false; 167162306a36Sopenharmony_ci tegra_host->pad_control_available = false; 167262306a36Sopenharmony_ci tegra_host->soc_data = soc_data; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_HAS_ANDROID_GPT_SECTOR) 167562306a36Sopenharmony_ci host->mmc->caps2 |= MMC_CAP2_ALT_GPT_TEGRA; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) { 167862306a36Sopenharmony_ci rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); 167962306a36Sopenharmony_ci if (rc == 0) 168062306a36Sopenharmony_ci host->mmc_host_ops.start_signal_voltage_switch = 168162306a36Sopenharmony_ci sdhci_tegra_start_signal_voltage_switch; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* Hook to periodically rerun pad calibration */ 168562306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 168662306a36Sopenharmony_ci host->mmc_host_ops.request = tegra_sdhci_request; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci host->mmc_host_ops.hs400_enhanced_strobe = 168962306a36Sopenharmony_ci tegra_sdhci_hs400_enhanced_strobe; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci if (!host->ops->platform_execute_tuning) 169262306a36Sopenharmony_ci host->mmc_host_ops.execute_tuning = 169362306a36Sopenharmony_ci tegra_sdhci_execute_hw_tuning; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci rc = mmc_of_parse(host->mmc); 169662306a36Sopenharmony_ci if (rc) 169762306a36Sopenharmony_ci goto err_parse_dt; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 170062306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_1_8V_DDR; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* HW busy detection is supported, but R1B responses are required. */ 170362306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci /* GPIO CD can be set as a wakeup source */ 170662306a36Sopenharmony_ci host->mmc->caps |= MMC_CAP_CD_WAKE; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci tegra_sdhci_parse_dt(host); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID && 171162306a36Sopenharmony_ci !tegra_dev_iommu_get_stream_id(&pdev->dev, &tegra_host->stream_id)) { 171262306a36Sopenharmony_ci dev_warn(mmc_dev(host->mmc), "missing IOMMU stream ID\n"); 171362306a36Sopenharmony_ci tegra_host->stream_id = 0x7f; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 171762306a36Sopenharmony_ci GPIOD_OUT_HIGH); 171862306a36Sopenharmony_ci if (IS_ERR(tegra_host->power_gpio)) { 171962306a36Sopenharmony_ci rc = PTR_ERR(tegra_host->power_gpio); 172062306a36Sopenharmony_ci goto err_power_req; 172162306a36Sopenharmony_ci } 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci /* 172462306a36Sopenharmony_ci * Tegra210 has a separate SDMMC_LEGACY_TM clock used for host 172562306a36Sopenharmony_ci * timeout clock and SW can choose TMCLK or SDCLK for hardware 172662306a36Sopenharmony_ci * data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT of 172762306a36Sopenharmony_ci * the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL. 172862306a36Sopenharmony_ci * 172962306a36Sopenharmony_ci * USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses 173062306a36Sopenharmony_ci * 12Mhz TMCLK which is advertised in host capability register. 173162306a36Sopenharmony_ci * With TMCLK of 12Mhz provides maximum data timeout period that can 173262306a36Sopenharmony_ci * be achieved is 11s better than using SDCLK for data timeout. 173362306a36Sopenharmony_ci * 173462306a36Sopenharmony_ci * So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's 173562306a36Sopenharmony_ci * supporting separate TMCLK. 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) { 173962306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, "tmclk"); 174062306a36Sopenharmony_ci if (IS_ERR(clk)) { 174162306a36Sopenharmony_ci rc = PTR_ERR(clk); 174262306a36Sopenharmony_ci if (rc == -EPROBE_DEFER) 174362306a36Sopenharmony_ci goto err_power_req; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc); 174662306a36Sopenharmony_ci clk = NULL; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci clk_set_rate(clk, 12000000); 175062306a36Sopenharmony_ci rc = clk_prepare_enable(clk); 175162306a36Sopenharmony_ci if (rc) { 175262306a36Sopenharmony_ci dev_err(&pdev->dev, 175362306a36Sopenharmony_ci "failed to enable tmclk: %d\n", rc); 175462306a36Sopenharmony_ci goto err_power_req; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci tegra_host->tmclk = clk; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci clk = devm_clk_get(mmc_dev(host->mmc), NULL); 176162306a36Sopenharmony_ci if (IS_ERR(clk)) { 176262306a36Sopenharmony_ci rc = dev_err_probe(&pdev->dev, PTR_ERR(clk), 176362306a36Sopenharmony_ci "failed to get clock\n"); 176462306a36Sopenharmony_ci goto err_clk_get; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci pltfm_host->clk = clk; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, 176962306a36Sopenharmony_ci "sdhci"); 177062306a36Sopenharmony_ci if (IS_ERR(tegra_host->rst)) { 177162306a36Sopenharmony_ci rc = PTR_ERR(tegra_host->rst); 177262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get reset control: %d\n", rc); 177362306a36Sopenharmony_ci goto err_rst_get; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); 177762306a36Sopenharmony_ci if (rc) 177862306a36Sopenharmony_ci goto err_rst_get; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 178162306a36Sopenharmony_ci rc = pm_runtime_resume_and_get(&pdev->dev); 178262306a36Sopenharmony_ci if (rc) 178362306a36Sopenharmony_ci goto err_pm_get; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci rc = reset_control_assert(tegra_host->rst); 178662306a36Sopenharmony_ci if (rc) 178762306a36Sopenharmony_ci goto err_rst_assert; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci usleep_range(2000, 4000); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci rc = reset_control_deassert(tegra_host->rst); 179262306a36Sopenharmony_ci if (rc) 179362306a36Sopenharmony_ci goto err_rst_assert; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci usleep_range(2000, 4000); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci rc = sdhci_tegra_add_host(host); 179862306a36Sopenharmony_ci if (rc) 179962306a36Sopenharmony_ci goto err_add_host; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci sdhci_tegra_program_stream_id(host); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci return 0; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_cierr_add_host: 180662306a36Sopenharmony_ci reset_control_assert(tegra_host->rst); 180762306a36Sopenharmony_cierr_rst_assert: 180862306a36Sopenharmony_ci pm_runtime_put_sync_suspend(&pdev->dev); 180962306a36Sopenharmony_cierr_pm_get: 181062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 181162306a36Sopenharmony_cierr_rst_get: 181262306a36Sopenharmony_cierr_clk_get: 181362306a36Sopenharmony_ci clk_disable_unprepare(tegra_host->tmclk); 181462306a36Sopenharmony_cierr_power_req: 181562306a36Sopenharmony_cierr_parse_dt: 181662306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 181762306a36Sopenharmony_ci return rc; 181862306a36Sopenharmony_ci} 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_cistatic void sdhci_tegra_remove(struct platform_device *pdev) 182162306a36Sopenharmony_ci{ 182262306a36Sopenharmony_ci struct sdhci_host *host = platform_get_drvdata(pdev); 182362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 182462306a36Sopenharmony_ci struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci sdhci_remove_host(host, 0); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci reset_control_assert(tegra_host->rst); 182962306a36Sopenharmony_ci usleep_range(2000, 4000); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci pm_runtime_put_sync_suspend(&pdev->dev); 183262306a36Sopenharmony_ci pm_runtime_force_suspend(&pdev->dev); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci clk_disable_unprepare(tegra_host->tmclk); 183562306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 183662306a36Sopenharmony_ci} 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_cistatic int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 184162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci clk_disable_unprepare(pltfm_host->clk); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci return 0; 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev) 184962306a36Sopenharmony_ci{ 185062306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 185162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci return clk_prepare_enable(pltfm_host->clk); 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 185762306a36Sopenharmony_cistatic int sdhci_tegra_suspend(struct device *dev) 185862306a36Sopenharmony_ci{ 185962306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 186062306a36Sopenharmony_ci int ret; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (host->mmc->caps2 & MMC_CAP2_CQE) { 186362306a36Sopenharmony_ci ret = cqhci_suspend(host->mmc); 186462306a36Sopenharmony_ci if (ret) 186562306a36Sopenharmony_ci return ret; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci ret = sdhci_suspend_host(host); 186962306a36Sopenharmony_ci if (ret) { 187062306a36Sopenharmony_ci cqhci_resume(host->mmc); 187162306a36Sopenharmony_ci return ret; 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 187562306a36Sopenharmony_ci if (ret) { 187662306a36Sopenharmony_ci sdhci_resume_host(host); 187762306a36Sopenharmony_ci cqhci_resume(host->mmc); 187862306a36Sopenharmony_ci return ret; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci return mmc_gpio_set_cd_wake(host->mmc, true); 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_cistatic int sdhci_tegra_resume(struct device *dev) 188562306a36Sopenharmony_ci{ 188662306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 188762306a36Sopenharmony_ci int ret; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci ret = mmc_gpio_set_cd_wake(host->mmc, false); 189062306a36Sopenharmony_ci if (ret) 189162306a36Sopenharmony_ci return ret; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci ret = pm_runtime_force_resume(dev); 189462306a36Sopenharmony_ci if (ret) 189562306a36Sopenharmony_ci return ret; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci sdhci_tegra_program_stream_id(host); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci ret = sdhci_resume_host(host); 190062306a36Sopenharmony_ci if (ret) 190162306a36Sopenharmony_ci goto disable_clk; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci if (host->mmc->caps2 & MMC_CAP2_CQE) { 190462306a36Sopenharmony_ci ret = cqhci_resume(host->mmc); 190562306a36Sopenharmony_ci if (ret) 190662306a36Sopenharmony_ci goto suspend_host; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci return 0; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cisuspend_host: 191262306a36Sopenharmony_ci sdhci_suspend_host(host); 191362306a36Sopenharmony_cidisable_clk: 191462306a36Sopenharmony_ci pm_runtime_force_suspend(dev); 191562306a36Sopenharmony_ci return ret; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci#endif 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_tegra_dev_pm_ops = { 192062306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, 192162306a36Sopenharmony_ci NULL) 192262306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume) 192362306a36Sopenharmony_ci}; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_cistatic struct platform_driver sdhci_tegra_driver = { 192662306a36Sopenharmony_ci .driver = { 192762306a36Sopenharmony_ci .name = "sdhci-tegra", 192862306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 192962306a36Sopenharmony_ci .of_match_table = sdhci_tegra_dt_match, 193062306a36Sopenharmony_ci .pm = &sdhci_tegra_dev_pm_ops, 193162306a36Sopenharmony_ci }, 193262306a36Sopenharmony_ci .probe = sdhci_tegra_probe, 193362306a36Sopenharmony_ci .remove_new = sdhci_tegra_remove, 193462306a36Sopenharmony_ci}; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cimodule_platform_driver(sdhci_tegra_driver); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI driver for Tegra"); 193962306a36Sopenharmony_ciMODULE_AUTHOR("Google, Inc."); 194062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1941