19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 203d2bfc8SOlof Johansson /* 303d2bfc8SOlof Johansson * Copyright (C) 2010 Google, Inc. 403d2bfc8SOlof Johansson */ 503d2bfc8SOlof Johansson 6e5c63d91SLucas Stach #include <linux/delay.h> 7b960bc44SNicolin Chen #include <linux/dma-mapping.h> 803d2bfc8SOlof Johansson #include <linux/err.h> 996547f5dSPaul Gortmaker #include <linux/module.h> 1003d2bfc8SOlof Johansson #include <linux/init.h> 11e7c07148SAapo Vienamo #include <linux/iopoll.h> 1203d2bfc8SOlof Johansson #include <linux/platform_device.h> 1303d2bfc8SOlof Johansson #include <linux/clk.h> 1403d2bfc8SOlof Johansson #include <linux/io.h> 1555cd65e4SStephen Warren #include <linux/of.h> 163e44a1a7SStephen Warren #include <linux/of_device.h> 1786ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h> 18d618978dSDmitry Osipenko #include <linux/pm_opp.h> 19d618978dSDmitry Osipenko #include <linux/pm_runtime.h> 2086ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h> 2120567be9SThierry Reding #include <linux/reset.h> 2203d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2303d2bfc8SOlof Johansson #include <linux/mmc/host.h> 24c3c2384cSLucas Stach #include <linux/mmc/mmc.h> 250aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 262391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 2761dad40eSAapo Vienamo #include <linux/ktime.h> 2803d2bfc8SOlof Johansson 29d618978dSDmitry Osipenko #include <soc/tegra/common.h> 30d618978dSDmitry Osipenko 31*83607844SBrian Norris #include "sdhci-cqhci.h" 3203d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 333c4019f9SSowjanya Komatineni #include "cqhci.h" 3403d2bfc8SOlof Johansson 35ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 3674cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 37c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 38c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 3941a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1f000000 4041a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24 41c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 4274cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 4374cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 4474cd42bcSLucas Stach 45dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL 0x104 46dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE BIT(31) 47dfc9700cSAapo Vienamo 48f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES 0x10c 49f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK 0x00003f00 50f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT 8 51f5313aaaSAapo Vienamo 52ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 535e958e4aSSowjanya Komatineni #define SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT BIT(0) 543145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 553145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 56ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 573145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 58ca5879d3SPavan Kunapuli 59bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG 0x1b0 60bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE BIT(31) 61bc5568bfSAapo Vienamo 62bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA 0x1bc 63bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE BIT(31) 64bc5568bfSAapo Vienamo 65d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 66d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 67ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK 0x03fc0000 68ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT 18 69ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK 0x00001fc0 70ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT 6 71ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK 0x000e000 72ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT 13 73ea8fc595SSowjanya Komatineni #define TRIES_128 2 74ea8fc595SSowjanya Komatineni #define TRIES_256 4 75ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK 0x7 76ea8fc595SSowjanya Komatineni 77ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0 0x1c4 78ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0 0x1C8 79ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1 0x1CC 80ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK 0xFF 81ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT 0x8 82ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE 32 83d4501d8eSAapo Vienamo 84e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 85e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START BIT(31) 86e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE BIT(29) 8751b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff 88e5c63d91SLucas Stach 899d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0 909d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f 919d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7 92212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31) 93de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK 0x07FFF000 949d548f11SAapo Vienamo 95e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec 96e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) 97e7c07148SAapo Vienamo 983e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 993e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 100ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 1017ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3) 1027ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4) 1037ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5) 10447fad46bSSowjanya Komatineni /* 10547fad46bSSowjanya Komatineni * HAS_PADCALIB NVQUIRK is for SoC's supporting auto calibration of pads 10647fad46bSSowjanya Komatineni * drive strength. 10747fad46bSSowjanya Komatineni */ 108e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB BIT(6) 10947fad46bSSowjanya Komatineni /* 11047fad46bSSowjanya Komatineni * NEEDS_PAD_CONTROL NVQUIRK is for SoC's having separate 3V3 and 1V8 pads. 11147fad46bSSowjanya Komatineni * 3V3/1V8 pad selection happens through pinctrl state selection depending 11247fad46bSSowjanya Komatineni * on the signaling mode. 11347fad46bSSowjanya Komatineni */ 11486ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) 115d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) 116c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9) 1173e44a1a7SStephen Warren 1188048822bSSowjanya Komatineni /* 1198048822bSSowjanya Komatineni * NVQUIRK_HAS_TMCLK is for SoC's having separate timeout clock for Tegra 1208048822bSSowjanya Komatineni * SDMMC hardware data timeout. 1218048822bSSowjanya Komatineni */ 1228048822bSSowjanya Komatineni #define NVQUIRK_HAS_TMCLK BIT(10) 1238048822bSSowjanya Komatineni 1241743fa54SDmitry Osipenko #define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11) 1251743fa54SDmitry Osipenko 1263c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ 1273c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 1283c4019f9SSowjanya Komatineni 1295ec6fa5aSAniruddha Tvs Rao #define SDHCI_TEGRA_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ 1305ec6fa5aSAniruddha Tvs Rao SDHCI_TRNS_BLK_CNT_EN | \ 1315ec6fa5aSAniruddha Tvs Rao SDHCI_TRNS_DMA) 1325ec6fa5aSAniruddha Tvs Rao 1333e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 1341db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 135b960bc44SNicolin Chen u64 dma_mask; 1363e44a1a7SStephen Warren u32 nvquirks; 137ea8fc595SSowjanya Komatineni u8 min_tap_delay; 138ea8fc595SSowjanya Komatineni u8 max_tap_delay; 1393e44a1a7SStephen Warren }; 1403e44a1a7SStephen Warren 14151b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */ 14251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets { 14351b77c8eSAapo Vienamo u32 pull_up_3v3; 14451b77c8eSAapo Vienamo u32 pull_down_3v3; 14551b77c8eSAapo Vienamo u32 pull_up_3v3_timeout; 14651b77c8eSAapo Vienamo u32 pull_down_3v3_timeout; 14751b77c8eSAapo Vienamo u32 pull_up_1v8; 14851b77c8eSAapo Vienamo u32 pull_down_1v8; 14951b77c8eSAapo Vienamo u32 pull_up_1v8_timeout; 15051b77c8eSAapo Vienamo u32 pull_down_1v8_timeout; 15151b77c8eSAapo Vienamo u32 pull_up_sdr104; 15251b77c8eSAapo Vienamo u32 pull_down_sdr104; 15351b77c8eSAapo Vienamo u32 pull_up_hs400; 15451b77c8eSAapo Vienamo u32 pull_down_hs400; 15551b77c8eSAapo Vienamo }; 15651b77c8eSAapo Vienamo 1573e44a1a7SStephen Warren struct sdhci_tegra { 1583e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 1592391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 1608048822bSSowjanya Komatineni struct clk *tmclk; 161a8e326a9SLucas Stach bool ddr_signaling; 162e5c63d91SLucas Stach bool pad_calib_required; 16386ac2f8bSAapo Vienamo bool pad_control_available; 16420567be9SThierry Reding 16520567be9SThierry Reding struct reset_control *rst; 16686ac2f8bSAapo Vienamo struct pinctrl *pinctrl_sdmmc; 16786ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_3v3; 16886ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_1v8; 169de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_3v3_drv; 170de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_1v8_drv; 17151b77c8eSAapo Vienamo 17251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets autocal_offsets; 17361dad40eSAapo Vienamo ktime_t last_calib; 17485c0da17SAapo Vienamo 17585c0da17SAapo Vienamo u32 default_tap; 17685c0da17SAapo Vienamo u32 default_trim; 177f5313aaaSAapo Vienamo u32 dqs_trim; 1783c4019f9SSowjanya Komatineni bool enable_hwcq; 179ea8fc595SSowjanya Komatineni unsigned long curr_clk_rate; 180ea8fc595SSowjanya Komatineni u8 tuned_tap_delay; 1813e44a1a7SStephen Warren }; 1823e44a1a7SStephen Warren 18303d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 18403d2bfc8SOlof Johansson { 1853e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1860734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1873e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1883e44a1a7SStephen Warren 1893e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 1903e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 19103d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 19203d2bfc8SOlof Johansson return SDHCI_SPEC_200; 19303d2bfc8SOlof Johansson } 19403d2bfc8SOlof Johansson 19503d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 19603d2bfc8SOlof Johansson } 19703d2bfc8SOlof Johansson 198352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 199352ee868SPavan Kunapuli { 200352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 201352ee868SPavan Kunapuli 202352ee868SPavan Kunapuli switch (reg) { 203352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 204352ee868SPavan Kunapuli /* 205352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 206352ee868SPavan Kunapuli * command write that is down below. 207352ee868SPavan Kunapuli */ 208352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 209352ee868SPavan Kunapuli return; 210352ee868SPavan Kunapuli case SDHCI_COMMAND: 211352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 212352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 213352ee868SPavan Kunapuli return; 214352ee868SPavan Kunapuli } 215352ee868SPavan Kunapuli 216352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 217352ee868SPavan Kunapuli } 218352ee868SPavan Kunapuli 21903d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 22003d2bfc8SOlof Johansson { 2213e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2220734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 2233e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 2243e44a1a7SStephen Warren 22503d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 22603d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 22703d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 22803d2bfc8SOlof Johansson */ 22903d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 23003d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 23103d2bfc8SOlof Johansson 23203d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 23303d2bfc8SOlof Johansson 2343e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 2353e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 23603d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 23703d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 23803d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 23903d2bfc8SOlof Johansson gap_ctrl |= 0x8; 24003d2bfc8SOlof Johansson else 24103d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 24203d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 24303d2bfc8SOlof Johansson } 24403d2bfc8SOlof Johansson } 24503d2bfc8SOlof Johansson 24638a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable) 24738a284d9SAapo Vienamo { 24838a284d9SAapo Vienamo bool status; 24938a284d9SAapo Vienamo u32 reg; 25038a284d9SAapo Vienamo 25138a284d9SAapo Vienamo reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 25238a284d9SAapo Vienamo status = !!(reg & SDHCI_CLOCK_CARD_EN); 25338a284d9SAapo Vienamo 25438a284d9SAapo Vienamo if (status == enable) 25538a284d9SAapo Vienamo return status; 25638a284d9SAapo Vienamo 25738a284d9SAapo Vienamo if (enable) 25838a284d9SAapo Vienamo reg |= SDHCI_CLOCK_CARD_EN; 25938a284d9SAapo Vienamo else 26038a284d9SAapo Vienamo reg &= ~SDHCI_CLOCK_CARD_EN; 26138a284d9SAapo Vienamo 26238a284d9SAapo Vienamo sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); 26338a284d9SAapo Vienamo 26438a284d9SAapo Vienamo return status; 26538a284d9SAapo Vienamo } 26638a284d9SAapo Vienamo 26738a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 26838a284d9SAapo Vienamo { 26938a284d9SAapo Vienamo bool is_tuning_cmd = 0; 27038a284d9SAapo Vienamo bool clk_enabled; 27138a284d9SAapo Vienamo u8 cmd; 27238a284d9SAapo Vienamo 27338a284d9SAapo Vienamo if (reg == SDHCI_COMMAND) { 27438a284d9SAapo Vienamo cmd = SDHCI_GET_CMD(val); 27538a284d9SAapo Vienamo is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK || 27638a284d9SAapo Vienamo cmd == MMC_SEND_TUNING_BLOCK_HS200; 27738a284d9SAapo Vienamo } 27838a284d9SAapo Vienamo 27938a284d9SAapo Vienamo if (is_tuning_cmd) 28038a284d9SAapo Vienamo clk_enabled = tegra_sdhci_configure_card_clk(host, 0); 28138a284d9SAapo Vienamo 28238a284d9SAapo Vienamo writew(val, host->ioaddr + reg); 28338a284d9SAapo Vienamo 28438a284d9SAapo Vienamo if (is_tuning_cmd) { 28538a284d9SAapo Vienamo udelay(1); 286ea8fc595SSowjanya Komatineni sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 28738a284d9SAapo Vienamo tegra_sdhci_configure_card_clk(host, clk_enabled); 28838a284d9SAapo Vienamo } 28938a284d9SAapo Vienamo } 29038a284d9SAapo Vienamo 2910f686ca9SDmitry Osipenko static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 2920f686ca9SDmitry Osipenko { 2930f686ca9SDmitry Osipenko /* 2940f686ca9SDmitry Osipenko * Write-enable shall be assumed if GPIO is missing in a board's 2950f686ca9SDmitry Osipenko * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on 2960f686ca9SDmitry Osipenko * Tegra. 2970f686ca9SDmitry Osipenko */ 2980f686ca9SDmitry Osipenko return mmc_gpio_get_ro(host->mmc); 2990f686ca9SDmitry Osipenko } 3000f686ca9SDmitry Osipenko 30186ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host) 30286ac2f8bSAapo Vienamo { 30386ac2f8bSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 30486ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 30586ac2f8bSAapo Vienamo int has_1v8, has_3v3; 30686ac2f8bSAapo Vienamo 30786ac2f8bSAapo Vienamo /* 30886ac2f8bSAapo Vienamo * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad 30986ac2f8bSAapo Vienamo * voltage configuration in order to perform voltage switching. This 31086ac2f8bSAapo Vienamo * means that valid pinctrl info is required on SDHCI instances capable 31186ac2f8bSAapo Vienamo * of performing voltage switching. Whether or not an SDHCI instance is 31286ac2f8bSAapo Vienamo * capable of voltage switching is determined based on the regulator. 31386ac2f8bSAapo Vienamo */ 31486ac2f8bSAapo Vienamo 31586ac2f8bSAapo Vienamo if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 31686ac2f8bSAapo Vienamo return true; 31786ac2f8bSAapo Vienamo 31886ac2f8bSAapo Vienamo if (IS_ERR(host->mmc->supply.vqmmc)) 31986ac2f8bSAapo Vienamo return false; 32086ac2f8bSAapo Vienamo 32186ac2f8bSAapo Vienamo has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 32286ac2f8bSAapo Vienamo 1700000, 1950000); 32386ac2f8bSAapo Vienamo 32486ac2f8bSAapo Vienamo has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 32586ac2f8bSAapo Vienamo 2700000, 3600000); 32686ac2f8bSAapo Vienamo 32786ac2f8bSAapo Vienamo if (has_1v8 == 1 && has_3v3 == 1) 32886ac2f8bSAapo Vienamo return tegra_host->pad_control_available; 32986ac2f8bSAapo Vienamo 33086ac2f8bSAapo Vienamo /* Fixed voltage, no pad control required. */ 33186ac2f8bSAapo Vienamo return true; 33286ac2f8bSAapo Vienamo } 33386ac2f8bSAapo Vienamo 334c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 335c2c09678SAapo Vienamo { 336c2c09678SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 337c2c09678SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 338c2c09678SAapo Vienamo const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 339c2c09678SAapo Vienamo bool card_clk_enabled = false; 340c2c09678SAapo Vienamo u32 reg; 341c2c09678SAapo Vienamo 342c2c09678SAapo Vienamo /* 343c2c09678SAapo Vienamo * Touching the tap values is a bit tricky on some SoC generations. 344c2c09678SAapo Vienamo * The quirk enables a workaround for a glitch that sometimes occurs if 345c2c09678SAapo Vienamo * the tap values are changed. 346c2c09678SAapo Vienamo */ 347c2c09678SAapo Vienamo 348c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) 349c2c09678SAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 350c2c09678SAapo Vienamo 351c2c09678SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 352c2c09678SAapo Vienamo reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 353c2c09678SAapo Vienamo reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 354c2c09678SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 355c2c09678SAapo Vienamo 356c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP && 357c2c09678SAapo Vienamo card_clk_enabled) { 358c2c09678SAapo Vienamo udelay(1); 359c2c09678SAapo Vienamo sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 360c2c09678SAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 361c2c09678SAapo Vienamo } 362c2c09678SAapo Vienamo } 363c2c09678SAapo Vienamo 36403231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 365ca5879d3SPavan Kunapuli { 366ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3670734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 368ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 3699d548f11SAapo Vienamo u32 misc_ctrl, clk_ctrl, pad_ctrl; 370ca5879d3SPavan Kunapuli 371*83607844SBrian Norris sdhci_and_cqhci_reset(host, mask); 37203231f9bSRussell King 373ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 374ca5879d3SPavan Kunapuli return; 375ca5879d3SPavan Kunapuli 376c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap); 377c2c09678SAapo Vienamo 3781b84def8SLucas Stach misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 3794f6aa326SJon Hunter clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 3804f6aa326SJon Hunter 3814f6aa326SJon Hunter misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | 3824f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR50 | 3834f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_DDR50 | 3844f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR104); 3854f6aa326SJon Hunter 38641a0b8d7SAapo Vienamo clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK | 38741a0b8d7SAapo Vienamo SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE); 3884f6aa326SJon Hunter 38986ac2f8bSAapo Vienamo if (tegra_sdhci_is_pad_and_regulator_valid(host)) { 390ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 3913145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 392ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 3937ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */ 3947ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 3957ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 3967ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 3977ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 3987ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 3997ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 400f571389cSMichał Mirosław if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 401c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 4024f6aa326SJon Hunter } 4034f6aa326SJon Hunter 40441a0b8d7SAapo Vienamo clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT; 40541a0b8d7SAapo Vienamo 4064f6aa326SJon Hunter sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 40774cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 40874cd42bcSLucas Stach 4099d548f11SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) { 4109d548f11SAapo Vienamo pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 4119d548f11SAapo Vienamo pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK; 4129d548f11SAapo Vienamo pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL; 4139d548f11SAapo Vienamo sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 4149d548f11SAapo Vienamo 415e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 4169d548f11SAapo Vienamo } 417e5c63d91SLucas Stach 418a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 419ca5879d3SPavan Kunapuli } 420ca5879d3SPavan Kunapuli 421212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable) 422212b0cf1SAapo Vienamo { 423212b0cf1SAapo Vienamo u32 val; 424212b0cf1SAapo Vienamo 425212b0cf1SAapo Vienamo /* 426212b0cf1SAapo Vienamo * Enable or disable the additional I/O pad used by the drive strength 427212b0cf1SAapo Vienamo * calibration process. 428212b0cf1SAapo Vienamo */ 429212b0cf1SAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 430212b0cf1SAapo Vienamo 431212b0cf1SAapo Vienamo if (enable) 432212b0cf1SAapo Vienamo val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 433212b0cf1SAapo Vienamo else 434212b0cf1SAapo Vienamo val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 435212b0cf1SAapo Vienamo 436212b0cf1SAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 437212b0cf1SAapo Vienamo 438212b0cf1SAapo Vienamo if (enable) 439212b0cf1SAapo Vienamo usleep_range(1, 2); 440212b0cf1SAapo Vienamo } 441212b0cf1SAapo Vienamo 44251b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host, 44351b77c8eSAapo Vienamo u16 pdpu) 44451b77c8eSAapo Vienamo { 44551b77c8eSAapo Vienamo u32 reg; 44651b77c8eSAapo Vienamo 44751b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 44851b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK; 44951b77c8eSAapo Vienamo reg |= pdpu; 45051b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 45151b77c8eSAapo Vienamo } 45251b77c8eSAapo Vienamo 453de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage, 454de25fa5aSSowjanya Komatineni bool state_drvupdn) 455de25fa5aSSowjanya Komatineni { 456de25fa5aSSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 457de25fa5aSSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 458de25fa5aSSowjanya Komatineni struct sdhci_tegra_autocal_offsets *offsets = 459de25fa5aSSowjanya Komatineni &tegra_host->autocal_offsets; 460de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_drvupdn = NULL; 461de25fa5aSSowjanya Komatineni int ret = 0; 462de25fa5aSSowjanya Komatineni u8 drvup = 0, drvdn = 0; 463de25fa5aSSowjanya Komatineni u32 reg; 464de25fa5aSSowjanya Komatineni 465de25fa5aSSowjanya Komatineni if (!state_drvupdn) { 466de25fa5aSSowjanya Komatineni /* PADS Drive Strength */ 467de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) { 468de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_1v8_drv) { 469de25fa5aSSowjanya Komatineni pinctrl_drvupdn = 470de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv; 471de25fa5aSSowjanya Komatineni } else { 472de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_1v8_timeout; 473de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_1v8_timeout; 474de25fa5aSSowjanya Komatineni } 475de25fa5aSSowjanya Komatineni } else { 476de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_3v3_drv) { 477de25fa5aSSowjanya Komatineni pinctrl_drvupdn = 478de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv; 479de25fa5aSSowjanya Komatineni } else { 480de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_3v3_timeout; 481de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_3v3_timeout; 482de25fa5aSSowjanya Komatineni } 483de25fa5aSSowjanya Komatineni } 484de25fa5aSSowjanya Komatineni 485de25fa5aSSowjanya Komatineni if (pinctrl_drvupdn != NULL) { 486de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 487de25fa5aSSowjanya Komatineni pinctrl_drvupdn); 488de25fa5aSSowjanya Komatineni if (ret < 0) 489de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 490de25fa5aSSowjanya Komatineni "failed pads drvupdn, ret: %d\n", ret); 491de25fa5aSSowjanya Komatineni } else if ((drvup) || (drvdn)) { 492de25fa5aSSowjanya Komatineni reg = sdhci_readl(host, 493de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 494de25fa5aSSowjanya Komatineni reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK; 495de25fa5aSSowjanya Komatineni reg |= (drvup << 20) | (drvdn << 12); 496de25fa5aSSowjanya Komatineni sdhci_writel(host, reg, 497de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 498de25fa5aSSowjanya Komatineni } 499de25fa5aSSowjanya Komatineni 500de25fa5aSSowjanya Komatineni } else { 501de25fa5aSSowjanya Komatineni /* Dual Voltage PADS Voltage selection */ 502de25fa5aSSowjanya Komatineni if (!tegra_host->pad_control_available) 503de25fa5aSSowjanya Komatineni return 0; 504de25fa5aSSowjanya Komatineni 505de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) { 506de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 507de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8); 508de25fa5aSSowjanya Komatineni if (ret < 0) 509de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 510de25fa5aSSowjanya Komatineni "setting 1.8V failed, ret: %d\n", ret); 511de25fa5aSSowjanya Komatineni } else { 512de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 513de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3); 514de25fa5aSSowjanya Komatineni if (ret < 0) 515de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 516de25fa5aSSowjanya Komatineni "setting 3.3V failed, ret: %d\n", ret); 517de25fa5aSSowjanya Komatineni } 518de25fa5aSSowjanya Komatineni } 519de25fa5aSSowjanya Komatineni 520de25fa5aSSowjanya Komatineni return ret; 521de25fa5aSSowjanya Komatineni } 522de25fa5aSSowjanya Komatineni 523e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) 524e5c63d91SLucas Stach { 52551b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 52651b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 52751b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets offsets = 52851b77c8eSAapo Vienamo tegra_host->autocal_offsets; 52951b77c8eSAapo Vienamo struct mmc_ios *ios = &host->mmc->ios; 530887bda8fSAapo Vienamo bool card_clk_enabled; 53151b77c8eSAapo Vienamo u16 pdpu; 532e7c07148SAapo Vienamo u32 reg; 533e7c07148SAapo Vienamo int ret; 534e5c63d91SLucas Stach 53551b77c8eSAapo Vienamo switch (ios->timing) { 53651b77c8eSAapo Vienamo case MMC_TIMING_UHS_SDR104: 53751b77c8eSAapo Vienamo pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104; 53851b77c8eSAapo Vienamo break; 53951b77c8eSAapo Vienamo case MMC_TIMING_MMC_HS400: 54051b77c8eSAapo Vienamo pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400; 54151b77c8eSAapo Vienamo break; 54251b77c8eSAapo Vienamo default: 54351b77c8eSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) 54451b77c8eSAapo Vienamo pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8; 54551b77c8eSAapo Vienamo else 54651b77c8eSAapo Vienamo pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3; 54751b77c8eSAapo Vienamo } 54851b77c8eSAapo Vienamo 549de25fa5aSSowjanya Komatineni /* Set initial offset before auto-calibration */ 55051b77c8eSAapo Vienamo tegra_sdhci_set_pad_autocal_offset(host, pdpu); 55151b77c8eSAapo Vienamo 552887bda8fSAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 553887bda8fSAapo Vienamo 554212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, true); 555212b0cf1SAapo Vienamo 556e7c07148SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 557e7c07148SAapo Vienamo reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; 558e7c07148SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 559e5c63d91SLucas Stach 560e7c07148SAapo Vienamo usleep_range(1, 2); 561e7c07148SAapo Vienamo /* 10 ms timeout */ 562e7c07148SAapo Vienamo ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS, 563e7c07148SAapo Vienamo reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE), 564e7c07148SAapo Vienamo 1000, 10000); 565e7c07148SAapo Vienamo 566212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, false); 567212b0cf1SAapo Vienamo 568887bda8fSAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 569887bda8fSAapo Vienamo 57051b77c8eSAapo Vienamo if (ret) { 571e7c07148SAapo Vienamo dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n"); 57251b77c8eSAapo Vienamo 573de25fa5aSSowjanya Komatineni /* Disable automatic cal and use fixed Drive Strengths */ 57451b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 57551b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_ENABLE; 57651b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 57751b77c8eSAapo Vienamo 578de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false); 579de25fa5aSSowjanya Komatineni if (ret < 0) 580de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 581de25fa5aSSowjanya Komatineni "Setting drive strengths failed: %d\n", ret); 58251b77c8eSAapo Vienamo } 58351b77c8eSAapo Vienamo } 58451b77c8eSAapo Vienamo 58551b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) 58651b77c8eSAapo Vienamo { 58751b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 58851b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 58951b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets *autocal = 59051b77c8eSAapo Vienamo &tegra_host->autocal_offsets; 59151b77c8eSAapo Vienamo int err; 59251b77c8eSAapo Vienamo 593bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 59451b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3", 59551b77c8eSAapo Vienamo &autocal->pull_up_3v3); 59651b77c8eSAapo Vienamo if (err) 59751b77c8eSAapo Vienamo autocal->pull_up_3v3 = 0; 59851b77c8eSAapo Vienamo 599bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 60051b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3", 60151b77c8eSAapo Vienamo &autocal->pull_down_3v3); 60251b77c8eSAapo Vienamo if (err) 60351b77c8eSAapo Vienamo autocal->pull_down_3v3 = 0; 60451b77c8eSAapo Vienamo 605bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 60651b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8", 60751b77c8eSAapo Vienamo &autocal->pull_up_1v8); 60851b77c8eSAapo Vienamo if (err) 60951b77c8eSAapo Vienamo autocal->pull_up_1v8 = 0; 61051b77c8eSAapo Vienamo 611bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 61251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8", 61351b77c8eSAapo Vienamo &autocal->pull_down_1v8); 61451b77c8eSAapo Vienamo if (err) 61551b77c8eSAapo Vienamo autocal->pull_down_1v8 = 0; 61651b77c8eSAapo Vienamo 617bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 618aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-up-offset-sdr104", 619aebbf577SSowjanya Komatineni &autocal->pull_up_sdr104); 620aebbf577SSowjanya Komatineni if (err) 621aebbf577SSowjanya Komatineni autocal->pull_up_sdr104 = autocal->pull_up_1v8; 622aebbf577SSowjanya Komatineni 623bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 624aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-down-offset-sdr104", 625aebbf577SSowjanya Komatineni &autocal->pull_down_sdr104); 626aebbf577SSowjanya Komatineni if (err) 627aebbf577SSowjanya Komatineni autocal->pull_down_sdr104 = autocal->pull_down_1v8; 628aebbf577SSowjanya Komatineni 629bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 630aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-up-offset-hs400", 631aebbf577SSowjanya Komatineni &autocal->pull_up_hs400); 632aebbf577SSowjanya Komatineni if (err) 633aebbf577SSowjanya Komatineni autocal->pull_up_hs400 = autocal->pull_up_1v8; 634aebbf577SSowjanya Komatineni 635bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 636aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-down-offset-hs400", 637aebbf577SSowjanya Komatineni &autocal->pull_down_hs400); 638aebbf577SSowjanya Komatineni if (err) 639aebbf577SSowjanya Komatineni autocal->pull_down_hs400 = autocal->pull_down_1v8; 640aebbf577SSowjanya Komatineni 641aebbf577SSowjanya Komatineni /* 642aebbf577SSowjanya Komatineni * Different fail-safe drive strength values based on the signaling 643aebbf577SSowjanya Komatineni * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls. 644aebbf577SSowjanya Komatineni * So, avoid reading below device tree properties for SoCs that don't 645aebbf577SSowjanya Komatineni * have NVQUIRK_NEEDS_PAD_CONTROL. 646aebbf577SSowjanya Komatineni */ 647aebbf577SSowjanya Komatineni if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 648aebbf577SSowjanya Komatineni return; 649aebbf577SSowjanya Komatineni 650bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 65151b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3-timeout", 6525ccf7f55SSowjanya Komatineni &autocal->pull_up_3v3_timeout); 653de25fa5aSSowjanya Komatineni if (err) { 654de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 655de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL)) 656de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 657de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 65851b77c8eSAapo Vienamo autocal->pull_up_3v3_timeout = 0; 659de25fa5aSSowjanya Komatineni } 66051b77c8eSAapo Vienamo 661bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 66251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3-timeout", 6635ccf7f55SSowjanya Komatineni &autocal->pull_down_3v3_timeout); 664de25fa5aSSowjanya Komatineni if (err) { 665de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 666de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL)) 667de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 668de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 66951b77c8eSAapo Vienamo autocal->pull_down_3v3_timeout = 0; 670de25fa5aSSowjanya Komatineni } 67151b77c8eSAapo Vienamo 672bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 67351b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8-timeout", 6745ccf7f55SSowjanya Komatineni &autocal->pull_up_1v8_timeout); 675de25fa5aSSowjanya Komatineni if (err) { 676de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 677de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL)) 678de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 679de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 68051b77c8eSAapo Vienamo autocal->pull_up_1v8_timeout = 0; 681de25fa5aSSowjanya Komatineni } 68251b77c8eSAapo Vienamo 683bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), 68451b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8-timeout", 6855ccf7f55SSowjanya Komatineni &autocal->pull_down_1v8_timeout); 686de25fa5aSSowjanya Komatineni if (err) { 687de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 688de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL)) 689de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 690de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 69151b77c8eSAapo Vienamo autocal->pull_down_1v8_timeout = 0; 692de25fa5aSSowjanya Komatineni } 693e5c63d91SLucas Stach } 694e5c63d91SLucas Stach 69561dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) 69661dad40eSAapo Vienamo { 69761dad40eSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 69861dad40eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 69961dad40eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 70061dad40eSAapo Vienamo ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib); 70161dad40eSAapo Vienamo 70261dad40eSAapo Vienamo /* 100 ms calibration interval is specified in the TRM */ 70361dad40eSAapo Vienamo if (ktime_to_ms(since_calib) > 100) { 70461dad40eSAapo Vienamo tegra_sdhci_pad_autocalib(host); 70561dad40eSAapo Vienamo tegra_host->last_calib = ktime_get(); 70661dad40eSAapo Vienamo } 70761dad40eSAapo Vienamo 70861dad40eSAapo Vienamo sdhci_request(mmc, mrq); 70961dad40eSAapo Vienamo } 71061dad40eSAapo Vienamo 711f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host) 71285c0da17SAapo Vienamo { 71385c0da17SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 71485c0da17SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 71585c0da17SAapo Vienamo int err; 71685c0da17SAapo Vienamo 717bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-tap", 71885c0da17SAapo Vienamo &tegra_host->default_tap); 71985c0da17SAapo Vienamo if (err) 72085c0da17SAapo Vienamo tegra_host->default_tap = 0; 72185c0da17SAapo Vienamo 722bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-trim", 72385c0da17SAapo Vienamo &tegra_host->default_trim); 72485c0da17SAapo Vienamo if (err) 72585c0da17SAapo Vienamo tegra_host->default_trim = 0; 726f5313aaaSAapo Vienamo 727bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,dqs-trim", 728f5313aaaSAapo Vienamo &tegra_host->dqs_trim); 729f5313aaaSAapo Vienamo if (err) 730f5313aaaSAapo Vienamo tegra_host->dqs_trim = 0x11; 73185c0da17SAapo Vienamo } 73285c0da17SAapo Vienamo 7333c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host) 7343c4019f9SSowjanya Komatineni { 7353c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 7363c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 7373c4019f9SSowjanya Komatineni 738bac53336SJisheng Zhang if (device_property_read_bool(mmc_dev(host->mmc), "supports-cqe")) 7393c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = true; 7403c4019f9SSowjanya Komatineni else 7413c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = false; 7423c4019f9SSowjanya Komatineni 7433c4019f9SSowjanya Komatineni tegra_sdhci_parse_pad_autocal_dt(host); 7443c4019f9SSowjanya Komatineni tegra_sdhci_parse_tap_and_trim(host); 7453c4019f9SSowjanya Komatineni } 7463c4019f9SSowjanya Komatineni 747a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 748a8e326a9SLucas Stach { 749a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 7500734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 751d618978dSDmitry Osipenko struct device *dev = mmc_dev(host->mmc); 752a8e326a9SLucas Stach unsigned long host_clk; 753d618978dSDmitry Osipenko int err; 754a8e326a9SLucas Stach 755a8e326a9SLucas Stach if (!clock) 7563491b690SLucas Stach return sdhci_set_clock(host, clock); 757a8e326a9SLucas Stach 75857d1654eSAapo Vienamo /* 75957d1654eSAapo Vienamo * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI 76057d1654eSAapo Vienamo * divider to be configured to divided the host clock by two. The SDHCI 76157d1654eSAapo Vienamo * clock divider is calculated as part of sdhci_set_clock() by 76257d1654eSAapo Vienamo * sdhci_calc_clk(). The divider is calculated from host->max_clk and 76357d1654eSAapo Vienamo * the requested clock rate. 76457d1654eSAapo Vienamo * 76557d1654eSAapo Vienamo * By setting the host->max_clk to clock * 2 the divider calculation 76657d1654eSAapo Vienamo * will always result in the correct value for DDR50/52 modes, 76757d1654eSAapo Vienamo * regardless of clock rate rounding, which may happen if the value 76857d1654eSAapo Vienamo * from clk_get_rate() is used. 76957d1654eSAapo Vienamo */ 770a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 771d618978dSDmitry Osipenko 772d618978dSDmitry Osipenko err = dev_pm_opp_set_rate(dev, host_clk); 773d618978dSDmitry Osipenko if (err) 774d618978dSDmitry Osipenko dev_err(dev, "failed to set clk rate to %luHz: %d\n", 775d618978dSDmitry Osipenko host_clk, err); 776d618978dSDmitry Osipenko 777b78870e7SPrathamesh Shete tegra_host->curr_clk_rate = clk_get_rate(pltfm_host->clk); 77857d1654eSAapo Vienamo if (tegra_host->ddr_signaling) 77957d1654eSAapo Vienamo host->max_clk = host_clk; 78057d1654eSAapo Vienamo else 781a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 782a8e326a9SLucas Stach 783e5c63d91SLucas Stach sdhci_set_clock(host, clock); 784e5c63d91SLucas Stach 785e5c63d91SLucas Stach if (tegra_host->pad_calib_required) { 786e5c63d91SLucas Stach tegra_sdhci_pad_autocalib(host); 787e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 788e5c63d91SLucas Stach } 789a8e326a9SLucas Stach } 790a8e326a9SLucas Stach 7914fc7261dSPrathamesh Shete static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, 7924fc7261dSPrathamesh Shete struct mmc_ios *ios) 7934fc7261dSPrathamesh Shete { 7944fc7261dSPrathamesh Shete struct sdhci_host *host = mmc_priv(mmc); 7954fc7261dSPrathamesh Shete u32 val; 7964fc7261dSPrathamesh Shete 7974fc7261dSPrathamesh Shete val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 7984fc7261dSPrathamesh Shete 7994fc7261dSPrathamesh Shete if (ios->enhanced_strobe) { 8004fc7261dSPrathamesh Shete val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 8014fc7261dSPrathamesh Shete /* 8024fc7261dSPrathamesh Shete * When CMD13 is sent from mmc_select_hs400es() after 8034fc7261dSPrathamesh Shete * switching to HS400ES mode, the bus is operating at 8044fc7261dSPrathamesh Shete * either MMC_HIGH_26_MAX_DTR or MMC_HIGH_52_MAX_DTR. 8054fc7261dSPrathamesh Shete * To meet Tegra SDHCI requirement at HS400ES mode, force SDHCI 8064fc7261dSPrathamesh Shete * interface clock to MMC_HS200_MAX_DTR (200 MHz) so that host 8074fc7261dSPrathamesh Shete * controller CAR clock and the interface clock are rate matched. 8084fc7261dSPrathamesh Shete */ 8094fc7261dSPrathamesh Shete tegra_sdhci_set_clock(host, MMC_HS200_MAX_DTR); 8104fc7261dSPrathamesh Shete } else { 8114fc7261dSPrathamesh Shete val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 8124fc7261dSPrathamesh Shete } 8134fc7261dSPrathamesh Shete 8144fc7261dSPrathamesh Shete sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 8154fc7261dSPrathamesh Shete } 8164fc7261dSPrathamesh Shete 81744350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 81844350993SAapo Vienamo { 81944350993SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 82044350993SAapo Vienamo 82144350993SAapo Vienamo return clk_round_rate(pltfm_host->clk, UINT_MAX); 82244350993SAapo Vienamo } 82344350993SAapo Vienamo 824f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim) 825f5313aaaSAapo Vienamo { 826f5313aaaSAapo Vienamo u32 val; 827f5313aaaSAapo Vienamo 828f5313aaaSAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 829f5313aaaSAapo Vienamo val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK; 830f5313aaaSAapo Vienamo val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT; 831f5313aaaSAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 832f5313aaaSAapo Vienamo } 833f5313aaaSAapo Vienamo 834bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host) 835bc5568bfSAapo Vienamo { 836bc5568bfSAapo Vienamo u32 reg; 837bc5568bfSAapo Vienamo int err; 838bc5568bfSAapo Vienamo 839bc5568bfSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 840bc5568bfSAapo Vienamo reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE; 841bc5568bfSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 842bc5568bfSAapo Vienamo 843bc5568bfSAapo Vienamo /* 1 ms sleep, 5 ms timeout */ 844bc5568bfSAapo Vienamo err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA, 845bc5568bfSAapo Vienamo reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE), 846bc5568bfSAapo Vienamo 1000, 5000); 847bc5568bfSAapo Vienamo if (err) 848bc5568bfSAapo Vienamo dev_err(mmc_dev(host->mmc), 849bc5568bfSAapo Vienamo "HS400 delay line calibration timed out\n"); 850bc5568bfSAapo Vienamo } 851bc5568bfSAapo Vienamo 852ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up, 853ea8fc595SSowjanya Komatineni u8 thd_low, u8 fixed_tap) 854ea8fc595SSowjanya Komatineni { 855ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 856ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 857ea8fc595SSowjanya Komatineni u32 val, tun_status; 858ea8fc595SSowjanya Komatineni u8 word, bit, edge1, tap, window; 859ea8fc595SSowjanya Komatineni bool tap_result; 860ea8fc595SSowjanya Komatineni bool start_fail = false; 861ea8fc595SSowjanya Komatineni bool start_pass = false; 862ea8fc595SSowjanya Komatineni bool end_pass = false; 863ea8fc595SSowjanya Komatineni bool first_fail = false; 864ea8fc595SSowjanya Komatineni bool first_pass = false; 865ea8fc595SSowjanya Komatineni u8 start_pass_tap = 0; 866ea8fc595SSowjanya Komatineni u8 end_pass_tap = 0; 867ea8fc595SSowjanya Komatineni u8 first_fail_tap = 0; 868ea8fc595SSowjanya Komatineni u8 first_pass_tap = 0; 869ea8fc595SSowjanya Komatineni u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE; 870ea8fc595SSowjanya Komatineni 871ea8fc595SSowjanya Komatineni /* 872ea8fc595SSowjanya Komatineni * Read auto-tuned results and extract good valid passing window by 873ea8fc595SSowjanya Komatineni * filtering out un-wanted bubble/partial/merged windows. 874ea8fc595SSowjanya Komatineni */ 875ea8fc595SSowjanya Komatineni for (word = 0; word < total_tuning_words; word++) { 876ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 877ea8fc595SSowjanya Komatineni val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK; 878ea8fc595SSowjanya Komatineni val |= word; 879ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 880ea8fc595SSowjanya Komatineni tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0); 881ea8fc595SSowjanya Komatineni bit = 0; 882ea8fc595SSowjanya Komatineni while (bit < TUNING_WORD_BIT_SIZE) { 883ea8fc595SSowjanya Komatineni tap = word * TUNING_WORD_BIT_SIZE + bit; 884ea8fc595SSowjanya Komatineni tap_result = tun_status & (1 << bit); 885ea8fc595SSowjanya Komatineni if (!tap_result && !start_fail) { 886ea8fc595SSowjanya Komatineni start_fail = true; 887ea8fc595SSowjanya Komatineni if (!first_fail) { 888ea8fc595SSowjanya Komatineni first_fail_tap = tap; 889ea8fc595SSowjanya Komatineni first_fail = true; 890ea8fc595SSowjanya Komatineni } 891ea8fc595SSowjanya Komatineni 892ea8fc595SSowjanya Komatineni } else if (tap_result && start_fail && !start_pass) { 893ea8fc595SSowjanya Komatineni start_pass_tap = tap; 894ea8fc595SSowjanya Komatineni start_pass = true; 895ea8fc595SSowjanya Komatineni if (!first_pass) { 896ea8fc595SSowjanya Komatineni first_pass_tap = tap; 897ea8fc595SSowjanya Komatineni first_pass = true; 898ea8fc595SSowjanya Komatineni } 899ea8fc595SSowjanya Komatineni 900ea8fc595SSowjanya Komatineni } else if (!tap_result && start_fail && start_pass && 901ea8fc595SSowjanya Komatineni !end_pass) { 902ea8fc595SSowjanya Komatineni end_pass_tap = tap - 1; 903ea8fc595SSowjanya Komatineni end_pass = true; 904ea8fc595SSowjanya Komatineni } else if (tap_result && start_pass && start_fail && 905ea8fc595SSowjanya Komatineni end_pass) { 906ea8fc595SSowjanya Komatineni window = end_pass_tap - start_pass_tap; 907ea8fc595SSowjanya Komatineni /* discard merged window and bubble window */ 908ea8fc595SSowjanya Komatineni if (window >= thd_up || window < thd_low) { 909ea8fc595SSowjanya Komatineni start_pass_tap = tap; 910ea8fc595SSowjanya Komatineni end_pass = false; 911ea8fc595SSowjanya Komatineni } else { 912ea8fc595SSowjanya Komatineni /* set tap at middle of valid window */ 913ea8fc595SSowjanya Komatineni tap = start_pass_tap + window / 2; 914ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = tap; 915ea8fc595SSowjanya Komatineni return; 916ea8fc595SSowjanya Komatineni } 917ea8fc595SSowjanya Komatineni } 918ea8fc595SSowjanya Komatineni 919ea8fc595SSowjanya Komatineni bit++; 920ea8fc595SSowjanya Komatineni } 921ea8fc595SSowjanya Komatineni } 922ea8fc595SSowjanya Komatineni 923ea8fc595SSowjanya Komatineni if (!first_fail) { 924d96dc68eSDan Carpenter WARN(1, "no edge detected, continue with hw tuned delay.\n"); 925ea8fc595SSowjanya Komatineni } else if (first_pass) { 926ea8fc595SSowjanya Komatineni /* set tap location at fixed tap relative to the first edge */ 927ea8fc595SSowjanya Komatineni edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2; 928ea8fc595SSowjanya Komatineni if (edge1 - 1 > fixed_tap) 929ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 - fixed_tap; 930ea8fc595SSowjanya Komatineni else 931ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 + fixed_tap; 932ea8fc595SSowjanya Komatineni } 933ea8fc595SSowjanya Komatineni } 934ea8fc595SSowjanya Komatineni 935ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host) 936ea8fc595SSowjanya Komatineni { 937ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 938ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 939ea8fc595SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 940ea8fc595SSowjanya Komatineni u32 avg_tap_dly, val, min_tap_dly, max_tap_dly; 941ea8fc595SSowjanya Komatineni u8 fixed_tap, start_tap, end_tap, window_width; 942ea8fc595SSowjanya Komatineni u8 thdupper, thdlower; 943ea8fc595SSowjanya Komatineni u8 num_iter; 944ea8fc595SSowjanya Komatineni u32 clk_rate_mhz, period_ps, bestcase, worstcase; 945ea8fc595SSowjanya Komatineni 946ea8fc595SSowjanya Komatineni /* retain HW tuned tap to use incase if no correction is needed */ 947ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 948ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >> 949ea8fc595SSowjanya Komatineni SDHCI_CLOCK_CTRL_TAP_SHIFT; 950ea8fc595SSowjanya Komatineni if (soc_data->min_tap_delay && soc_data->max_tap_delay) { 951ea8fc595SSowjanya Komatineni min_tap_dly = soc_data->min_tap_delay; 952ea8fc595SSowjanya Komatineni max_tap_dly = soc_data->max_tap_delay; 953ea8fc595SSowjanya Komatineni clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC; 954ea8fc595SSowjanya Komatineni period_ps = USEC_PER_SEC / clk_rate_mhz; 955ea8fc595SSowjanya Komatineni bestcase = period_ps / min_tap_dly; 956ea8fc595SSowjanya Komatineni worstcase = period_ps / max_tap_dly; 957ea8fc595SSowjanya Komatineni /* 958ea8fc595SSowjanya Komatineni * Upper and Lower bound thresholds used to detect merged and 959ea8fc595SSowjanya Komatineni * bubble windows 960ea8fc595SSowjanya Komatineni */ 961ea8fc595SSowjanya Komatineni thdupper = (2 * worstcase + bestcase) / 2; 962ea8fc595SSowjanya Komatineni thdlower = worstcase / 4; 963ea8fc595SSowjanya Komatineni /* 964ea8fc595SSowjanya Komatineni * fixed tap is used when HW tuning result contains single edge 965ea8fc595SSowjanya Komatineni * and tap is set at fixed tap delay relative to the first edge 966ea8fc595SSowjanya Komatineni */ 967ea8fc595SSowjanya Komatineni avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly); 968ea8fc595SSowjanya Komatineni fixed_tap = avg_tap_dly / 2; 969ea8fc595SSowjanya Komatineni 970ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1); 971ea8fc595SSowjanya Komatineni start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 972ea8fc595SSowjanya Komatineni end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) & 973ea8fc595SSowjanya Komatineni SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 974ea8fc595SSowjanya Komatineni window_width = end_tap - start_tap; 975ea8fc595SSowjanya Komatineni num_iter = host->tuning_loop_count; 976ea8fc595SSowjanya Komatineni /* 977ea8fc595SSowjanya Komatineni * partial window includes edges of the tuning range. 978ea8fc595SSowjanya Komatineni * merged window includes more taps so window width is higher 979ea8fc595SSowjanya Komatineni * than upper threshold. 980ea8fc595SSowjanya Komatineni */ 981ea8fc595SSowjanya Komatineni if (start_tap == 0 || (end_tap == (num_iter - 1)) || 982ea8fc595SSowjanya Komatineni (end_tap == num_iter - 2) || window_width >= thdupper) { 983ea8fc595SSowjanya Komatineni pr_debug("%s: Apply tuning correction\n", 984ea8fc595SSowjanya Komatineni mmc_hostname(host->mmc)); 985ea8fc595SSowjanya Komatineni tegra_sdhci_tap_correction(host, thdupper, thdlower, 986ea8fc595SSowjanya Komatineni fixed_tap); 987ea8fc595SSowjanya Komatineni } 988ea8fc595SSowjanya Komatineni } 989ea8fc595SSowjanya Komatineni 990ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 991ea8fc595SSowjanya Komatineni } 992ea8fc595SSowjanya Komatineni 993ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode) 994ea8fc595SSowjanya Komatineni { 995ea8fc595SSowjanya Komatineni struct sdhci_host *host = mmc_priv(mmc); 996ea8fc595SSowjanya Komatineni int err; 997ea8fc595SSowjanya Komatineni 998ea8fc595SSowjanya Komatineni err = sdhci_execute_tuning(mmc, opcode); 999ea8fc595SSowjanya Komatineni if (!err && !host->tuning_err) 1000ea8fc595SSowjanya Komatineni tegra_sdhci_post_tuning(host); 1001ea8fc595SSowjanya Komatineni 1002ea8fc595SSowjanya Komatineni return err; 1003ea8fc595SSowjanya Komatineni } 1004ea8fc595SSowjanya Komatineni 1005c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 1006c2c09678SAapo Vienamo unsigned timing) 1007c3c2384cSLucas Stach { 1008d4501d8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1009d4501d8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1010c2c09678SAapo Vienamo bool set_default_tap = false; 1011f5313aaaSAapo Vienamo bool set_dqs_trim = false; 1012bc5568bfSAapo Vienamo bool do_hs400_dll_cal = false; 1013ea8fc595SSowjanya Komatineni u8 iter = TRIES_256; 1014ea8fc595SSowjanya Komatineni u32 val; 1015c3c2384cSLucas Stach 101692cd1667SSowjanya Komatineni tegra_host->ddr_signaling = false; 1017c2c09678SAapo Vienamo switch (timing) { 1018c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR50: 1019ea8fc595SSowjanya Komatineni break; 1020c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR104: 1021c2c09678SAapo Vienamo case MMC_TIMING_MMC_HS200: 1022c2c09678SAapo Vienamo /* Don't set default tap on tunable modes. */ 1023ea8fc595SSowjanya Komatineni iter = TRIES_128; 1024c2c09678SAapo Vienamo break; 1025f5313aaaSAapo Vienamo case MMC_TIMING_MMC_HS400: 1026f5313aaaSAapo Vienamo set_dqs_trim = true; 1027bc5568bfSAapo Vienamo do_hs400_dll_cal = true; 1028ea8fc595SSowjanya Komatineni iter = TRIES_128; 1029f5313aaaSAapo Vienamo break; 1030c2c09678SAapo Vienamo case MMC_TIMING_MMC_DDR52: 1031c2c09678SAapo Vienamo case MMC_TIMING_UHS_DDR50: 1032c2c09678SAapo Vienamo tegra_host->ddr_signaling = true; 1033c2c09678SAapo Vienamo set_default_tap = true; 1034c2c09678SAapo Vienamo break; 1035c2c09678SAapo Vienamo default: 1036c2c09678SAapo Vienamo set_default_tap = true; 1037c2c09678SAapo Vienamo break; 1038d4501d8eSAapo Vienamo } 1039c2c09678SAapo Vienamo 1040ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 1041ea8fc595SSowjanya Komatineni val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK | 1042ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK | 1043ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK); 1044ea8fc595SSowjanya Komatineni val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT | 1045ea8fc595SSowjanya Komatineni 0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT | 1046ea8fc595SSowjanya Komatineni 1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT); 1047ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 1048ea8fc595SSowjanya Komatineni sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0); 1049ea8fc595SSowjanya Komatineni 1050ea8fc595SSowjanya Komatineni host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256; 1051ea8fc595SSowjanya Komatineni 1052c2c09678SAapo Vienamo sdhci_set_uhs_signaling(host, timing); 1053c2c09678SAapo Vienamo 1054c2c09678SAapo Vienamo tegra_sdhci_pad_autocalib(host); 1055c2c09678SAapo Vienamo 1056ea8fc595SSowjanya Komatineni if (tegra_host->tuned_tap_delay && !set_default_tap) 1057ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 1058ea8fc595SSowjanya Komatineni else 1059c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap); 1060f5313aaaSAapo Vienamo 1061f5313aaaSAapo Vienamo if (set_dqs_trim) 1062f5313aaaSAapo Vienamo tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim); 1063bc5568bfSAapo Vienamo 1064bc5568bfSAapo Vienamo if (do_hs400_dll_cal) 1065bc5568bfSAapo Vienamo tegra_sdhci_hs400_dll_cal(host); 1066c3c2384cSLucas Stach } 1067c3c2384cSLucas Stach 1068c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 1069c3c2384cSLucas Stach { 1070c3c2384cSLucas Stach unsigned int min, max; 1071c3c2384cSLucas Stach 1072c3c2384cSLucas Stach /* 1073c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 1074c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 1075c3c2384cSLucas Stach * according to the TRM. 1076c3c2384cSLucas Stach */ 1077c3c2384cSLucas Stach min = 10; 1078c3c2384cSLucas Stach while (min < 255) { 1079c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 1080c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 1081c3c2384cSLucas Stach break; 1082c3c2384cSLucas Stach min++; 1083c3c2384cSLucas Stach } 1084c3c2384cSLucas Stach 1085c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 1086c3c2384cSLucas Stach max = min + 1; 1087c3c2384cSLucas Stach while (max < 255) { 1088c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 1089c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 1090c3c2384cSLucas Stach max--; 1091c3c2384cSLucas Stach break; 1092c3c2384cSLucas Stach } 1093c3c2384cSLucas Stach max++; 1094c3c2384cSLucas Stach } 1095c3c2384cSLucas Stach 1096c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 1097c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 1098c3c2384cSLucas Stach 1099c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 1100c3c2384cSLucas Stach } 1101c3c2384cSLucas Stach 110286ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc, 110386ac2f8bSAapo Vienamo struct mmc_ios *ios) 110486ac2f8bSAapo Vienamo { 110586ac2f8bSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 110644babea2SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 110744babea2SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 110886ac2f8bSAapo Vienamo int ret = 0; 110986ac2f8bSAapo Vienamo 111086ac2f8bSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { 1111de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 111286ac2f8bSAapo Vienamo if (ret < 0) 111386ac2f8bSAapo Vienamo return ret; 111486ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 111586ac2f8bSAapo Vienamo } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { 111686ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 111786ac2f8bSAapo Vienamo if (ret < 0) 111886ac2f8bSAapo Vienamo return ret; 1119de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 112086ac2f8bSAapo Vienamo } 112186ac2f8bSAapo Vienamo 112244babea2SAapo Vienamo if (tegra_host->pad_calib_required) 112344babea2SAapo Vienamo tegra_sdhci_pad_autocalib(host); 112444babea2SAapo Vienamo 112586ac2f8bSAapo Vienamo return ret; 112686ac2f8bSAapo Vienamo } 112786ac2f8bSAapo Vienamo 112886ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev, 112986ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host) 113086ac2f8bSAapo Vienamo { 113186ac2f8bSAapo Vienamo tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev); 113286ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_sdmmc)) { 113386ac2f8bSAapo Vienamo dev_dbg(dev, "No pinctrl info, err: %ld\n", 113486ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_sdmmc)); 113586ac2f8bSAapo Vienamo return -1; 113686ac2f8bSAapo Vienamo } 113786ac2f8bSAapo Vienamo 1138de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state( 1139de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv"); 1140de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) { 1141de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV) 1142de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = NULL; 1143de25fa5aSSowjanya Komatineni } 1144de25fa5aSSowjanya Komatineni 1145de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state( 1146de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv"); 1147de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) { 1148de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV) 1149de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = NULL; 1150de25fa5aSSowjanya Komatineni } 1151de25fa5aSSowjanya Komatineni 115286ac2f8bSAapo Vienamo tegra_host->pinctrl_state_3v3 = 115386ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3"); 115486ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_3v3)) { 115586ac2f8bSAapo Vienamo dev_warn(dev, "Missing 3.3V pad state, err: %ld\n", 115686ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_state_3v3)); 115786ac2f8bSAapo Vienamo return -1; 115886ac2f8bSAapo Vienamo } 115986ac2f8bSAapo Vienamo 116086ac2f8bSAapo Vienamo tegra_host->pinctrl_state_1v8 = 116186ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8"); 116286ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_1v8)) { 116386ac2f8bSAapo Vienamo dev_warn(dev, "Missing 1.8V pad state, err: %ld\n", 1164e5378247SYueHaibing PTR_ERR(tegra_host->pinctrl_state_1v8)); 116586ac2f8bSAapo Vienamo return -1; 116686ac2f8bSAapo Vienamo } 116786ac2f8bSAapo Vienamo 116886ac2f8bSAapo Vienamo tegra_host->pad_control_available = true; 116986ac2f8bSAapo Vienamo 117086ac2f8bSAapo Vienamo return 0; 117186ac2f8bSAapo Vienamo } 117286ac2f8bSAapo Vienamo 1173e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host) 1174e5c63d91SLucas Stach { 1175e5c63d91SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1176e5c63d91SLucas Stach struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1177e5c63d91SLucas Stach const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1178e5c63d91SLucas Stach 1179e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 1180e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 1181e5c63d91SLucas Stach } 1182e5c63d91SLucas Stach 1183b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg) 1184b7754428SSowjanya Komatineni { 1185b7754428SSowjanya Komatineni struct mmc_host *mmc = cq_host->mmc; 11865ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc); 1187b7754428SSowjanya Komatineni u8 ctrl; 1188b7754428SSowjanya Komatineni ktime_t timeout; 1189b7754428SSowjanya Komatineni bool timed_out; 1190b7754428SSowjanya Komatineni 1191b7754428SSowjanya Komatineni /* 1192b7754428SSowjanya Komatineni * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to 1193b7754428SSowjanya Komatineni * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need 1194b7754428SSowjanya Komatineni * to be re-configured. 1195b7754428SSowjanya Komatineni * Tegra CQHCI/SDHCI prevents write access to block size register when 1196b7754428SSowjanya Komatineni * CQE is unhalted. So handling CQE resume sequence here to configure 1197b7754428SSowjanya Komatineni * SDHCI block registers prior to exiting CQE halt state. 1198b7754428SSowjanya Komatineni */ 1199b7754428SSowjanya Komatineni if (reg == CQHCI_CTL && !(val & CQHCI_HALT) && 1200b7754428SSowjanya Komatineni cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) { 12015ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); 1202b7754428SSowjanya Komatineni sdhci_cqe_enable(mmc); 1203b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1204b7754428SSowjanya Komatineni timeout = ktime_add_us(ktime_get(), 50); 1205b7754428SSowjanya Komatineni while (1) { 1206b7754428SSowjanya Komatineni timed_out = ktime_compare(ktime_get(), timeout) > 0; 1207b7754428SSowjanya Komatineni ctrl = cqhci_readl(cq_host, CQHCI_CTL); 1208b7754428SSowjanya Komatineni if (!(ctrl & CQHCI_HALT) || timed_out) 1209b7754428SSowjanya Komatineni break; 1210b7754428SSowjanya Komatineni } 1211b7754428SSowjanya Komatineni /* 1212b7754428SSowjanya Komatineni * CQE usually resumes very quick, but incase if Tegra CQE 1213b7754428SSowjanya Komatineni * doesn't resume retry unhalt. 1214b7754428SSowjanya Komatineni */ 1215b7754428SSowjanya Komatineni if (timed_out) 1216b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1217b7754428SSowjanya Komatineni } else { 1218b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1219b7754428SSowjanya Komatineni } 1220b7754428SSowjanya Komatineni } 1221b7754428SSowjanya Komatineni 1222c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc, 1223c6e7ab90SSowjanya Komatineni struct mmc_request *mrq, u64 *data) 1224c6e7ab90SSowjanya Komatineni { 1225c6e7ab90SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc)); 1226c6e7ab90SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1227c6e7ab90SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1228c6e7ab90SSowjanya Komatineni 1229c6e7ab90SSowjanya Komatineni if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING && 1230c6e7ab90SSowjanya Komatineni mrq->cmd->flags & MMC_RSP_R1B) 1231c6e7ab90SSowjanya Komatineni *data |= CQHCI_CMD_TIMING(1); 1232c6e7ab90SSowjanya Komatineni } 1233c6e7ab90SSowjanya Komatineni 12343c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc) 12353c4019f9SSowjanya Komatineni { 12363c4019f9SSowjanya Komatineni struct cqhci_host *cq_host = mmc->cqe_private; 12375ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc); 1238b7754428SSowjanya Komatineni u32 val; 12393c4019f9SSowjanya Komatineni 12403c4019f9SSowjanya Komatineni /* 1241b7754428SSowjanya Komatineni * Tegra CQHCI/SDMMC design prevents write access to sdhci block size 1242b7754428SSowjanya Komatineni * register when CQE is enabled and unhalted. 1243b7754428SSowjanya Komatineni * CQHCI driver enables CQE prior to activation, so disable CQE before 1244b7754428SSowjanya Komatineni * programming block size in sdhci controller and enable it back. 12453c4019f9SSowjanya Komatineni */ 1246b7754428SSowjanya Komatineni if (!cq_host->activated) { 1247b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_CFG); 1248b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE) 1249b7754428SSowjanya Komatineni cqhci_writel(cq_host, (val & ~CQHCI_ENABLE), 1250b7754428SSowjanya Komatineni CQHCI_CFG); 12515ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); 12523c4019f9SSowjanya Komatineni sdhci_cqe_enable(mmc); 1253b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE) 1254b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_CFG); 1255b7754428SSowjanya Komatineni } 12563c4019f9SSowjanya Komatineni 1257b7754428SSowjanya Komatineni /* 1258b7754428SSowjanya Komatineni * CMD CRC errors are seen sometimes with some eMMC devices when status 1259b7754428SSowjanya Komatineni * command is sent during transfer of last data block which is the 1260b7754428SSowjanya Komatineni * default case as send status command block counter (CBC) is 1. 1261b7754428SSowjanya Komatineni * Recommended fix to set CBC to 0 allowing send status command only 1262b7754428SSowjanya Komatineni * when data lines are idle. 1263b7754428SSowjanya Komatineni */ 1264b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_SSC1); 1265b7754428SSowjanya Komatineni val &= ~CQHCI_SSC1_CBC_MASK; 1266b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_SSC1); 12673c4019f9SSowjanya Komatineni } 12683c4019f9SSowjanya Komatineni 12693c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc) 12703c4019f9SSowjanya Komatineni { 12713c4019f9SSowjanya Komatineni sdhci_dumpregs(mmc_priv(mmc)); 12723c4019f9SSowjanya Komatineni } 12733c4019f9SSowjanya Komatineni 12743c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask) 12753c4019f9SSowjanya Komatineni { 12763c4019f9SSowjanya Komatineni int cmd_error = 0; 12773c4019f9SSowjanya Komatineni int data_error = 0; 12783c4019f9SSowjanya Komatineni 12793c4019f9SSowjanya Komatineni if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 12803c4019f9SSowjanya Komatineni return intmask; 12813c4019f9SSowjanya Komatineni 12823c4019f9SSowjanya Komatineni cqhci_irq(host->mmc, intmask, cmd_error, data_error); 12833c4019f9SSowjanya Komatineni 12843c4019f9SSowjanya Komatineni return 0; 12853c4019f9SSowjanya Komatineni } 12863c4019f9SSowjanya Komatineni 12875e958e4aSSowjanya Komatineni static void tegra_sdhci_set_timeout(struct sdhci_host *host, 12885e958e4aSSowjanya Komatineni struct mmc_command *cmd) 12895e958e4aSSowjanya Komatineni { 12905e958e4aSSowjanya Komatineni u32 val; 12915e958e4aSSowjanya Komatineni 12925e958e4aSSowjanya Komatineni /* 12935e958e4aSSowjanya Komatineni * HW busy detection timeout is based on programmed data timeout 12945e958e4aSSowjanya Komatineni * counter and maximum supported timeout is 11s which may not be 12955e958e4aSSowjanya Komatineni * enough for long operations like cache flush, sleep awake, erase. 12965e958e4aSSowjanya Komatineni * 12975e958e4aSSowjanya Komatineni * ERASE_TIMEOUT_LIMIT bit of VENDOR_MISC_CTRL register allows 12985e958e4aSSowjanya Komatineni * host controller to wait for busy state until the card is busy 12995e958e4aSSowjanya Komatineni * without HW timeout. 13005e958e4aSSowjanya Komatineni * 13015e958e4aSSowjanya Komatineni * So, use infinite busy wait mode for operations that may take 13025e958e4aSSowjanya Komatineni * more than maximum HW busy timeout of 11s otherwise use finite 13035e958e4aSSowjanya Komatineni * busy wait mode. 13045e958e4aSSowjanya Komatineni */ 13055e958e4aSSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 1306fcc541feSWolfram Sang if (cmd && cmd->busy_timeout >= 11 * MSEC_PER_SEC) 13075e958e4aSSowjanya Komatineni val |= SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; 13085e958e4aSSowjanya Komatineni else 13095e958e4aSSowjanya Komatineni val &= ~SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; 13105e958e4aSSowjanya Komatineni sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_MISC_CTRL); 13115e958e4aSSowjanya Komatineni 13125e958e4aSSowjanya Komatineni __sdhci_set_timeout(host, cmd); 13135e958e4aSSowjanya Komatineni } 13145e958e4aSSowjanya Komatineni 13155ec6fa5aSAniruddha Tvs Rao static void sdhci_tegra_cqe_pre_enable(struct mmc_host *mmc) 13165ec6fa5aSAniruddha Tvs Rao { 13175ec6fa5aSAniruddha Tvs Rao struct cqhci_host *cq_host = mmc->cqe_private; 13185ec6fa5aSAniruddha Tvs Rao u32 reg; 13195ec6fa5aSAniruddha Tvs Rao 13205ec6fa5aSAniruddha Tvs Rao reg = cqhci_readl(cq_host, CQHCI_CFG); 13215ec6fa5aSAniruddha Tvs Rao reg |= CQHCI_ENABLE; 13225ec6fa5aSAniruddha Tvs Rao cqhci_writel(cq_host, reg, CQHCI_CFG); 13235ec6fa5aSAniruddha Tvs Rao } 13245ec6fa5aSAniruddha Tvs Rao 13255ec6fa5aSAniruddha Tvs Rao static void sdhci_tegra_cqe_post_disable(struct mmc_host *mmc) 13265ec6fa5aSAniruddha Tvs Rao { 13275ec6fa5aSAniruddha Tvs Rao struct cqhci_host *cq_host = mmc->cqe_private; 13285ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc); 13295ec6fa5aSAniruddha Tvs Rao u32 reg; 13305ec6fa5aSAniruddha Tvs Rao 13315ec6fa5aSAniruddha Tvs Rao reg = cqhci_readl(cq_host, CQHCI_CFG); 13325ec6fa5aSAniruddha Tvs Rao reg &= ~CQHCI_ENABLE; 13335ec6fa5aSAniruddha Tvs Rao cqhci_writel(cq_host, reg, CQHCI_CFG); 13345ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); 13355ec6fa5aSAniruddha Tvs Rao } 13365ec6fa5aSAniruddha Tvs Rao 13373c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { 1338b7754428SSowjanya Komatineni .write_l = tegra_cqhci_writel, 13393c4019f9SSowjanya Komatineni .enable = sdhci_tegra_cqe_enable, 13403c4019f9SSowjanya Komatineni .disable = sdhci_cqe_disable, 13413c4019f9SSowjanya Komatineni .dumpregs = sdhci_tegra_dumpregs, 1342c6e7ab90SSowjanya Komatineni .update_dcmd_desc = sdhci_tegra_update_dcmd_desc, 13435ec6fa5aSAniruddha Tvs Rao .pre_enable = sdhci_tegra_cqe_pre_enable, 13445ec6fa5aSAniruddha Tvs Rao .post_disable = sdhci_tegra_cqe_post_disable, 13453c4019f9SSowjanya Komatineni }; 13463c4019f9SSowjanya Komatineni 1347b960bc44SNicolin Chen static int tegra_sdhci_set_dma_mask(struct sdhci_host *host) 1348b960bc44SNicolin Chen { 1349b960bc44SNicolin Chen struct sdhci_pltfm_host *platform = sdhci_priv(host); 1350b960bc44SNicolin Chen struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform); 1351b960bc44SNicolin Chen const struct sdhci_tegra_soc_data *soc = tegra->soc_data; 1352b960bc44SNicolin Chen struct device *dev = mmc_dev(host->mmc); 1353b960bc44SNicolin Chen 1354b960bc44SNicolin Chen if (soc->dma_mask) 1355b960bc44SNicolin Chen return dma_set_mask_and_coherent(dev, soc->dma_mask); 1356b960bc44SNicolin Chen 1357b960bc44SNicolin Chen return 0; 1358b960bc44SNicolin Chen } 1359b960bc44SNicolin Chen 1360c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 13610f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 136285d6509dSShawn Guo .read_w = tegra_sdhci_readw, 136385d6509dSShawn Guo .write_l = tegra_sdhci_writel, 1364a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 1365b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 136614b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 136703231f9bSRussell King .reset = tegra_sdhci_reset, 1368c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 1369a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 1370e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 137144350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 137285d6509dSShawn Guo }; 137303d2bfc8SOlof Johansson 13741db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 137585d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 137685d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 137785d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 1378f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1379f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 138085d6509dSShawn Guo .ops = &tegra_sdhci_ops, 138185d6509dSShawn Guo }; 138285d6509dSShawn Guo 1383d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 13843e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 1385b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 13863e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 13871743fa54SDmitry Osipenko NVQUIRK_HAS_ANDROID_GPT_SECTOR | 13883e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 13893e44a1a7SStephen Warren }; 13903e44a1a7SStephen Warren 13911db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 13923e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 13933e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 13943e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 13953e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 1396f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1397f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1398127407e3SStefan Agner .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 1399726df1d5SStefan Agner SDHCI_QUIRK2_BROKEN_HS200 | 1400726df1d5SStefan Agner /* 1401726df1d5SStefan Agner * Auto-CMD23 leads to "Got command interrupt 0x00010000 even 1402726df1d5SStefan Agner * though no command operation was in progress." 1403726df1d5SStefan Agner * 1404726df1d5SStefan Agner * The exact reason is unknown, as the same hardware seems 1405726df1d5SStefan Agner * to support Auto CMD23 on a downstream 3.1 kernel. 1406726df1d5SStefan Agner */ 1407726df1d5SStefan Agner SDHCI_QUIRK2_ACMD23_BROKEN, 14083e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 14093e44a1a7SStephen Warren }; 14103e44a1a7SStephen Warren 1411d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 14123e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 1413b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 14143145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 14157ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 1416e5c63d91SLucas Stach NVQUIRK_ENABLE_SDR104 | 14171743fa54SDmitry Osipenko NVQUIRK_HAS_ANDROID_GPT_SECTOR | 1418e5c63d91SLucas Stach NVQUIRK_HAS_PADCALIB, 14193e44a1a7SStephen Warren }; 14203e44a1a7SStephen Warren 142101df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 14220f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 142301df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 142401df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 142501df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 1426a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 1427b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 142814b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 142901df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 1430c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 1431a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 1432e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 143344350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 143401df7ecdSRhyland Klein }; 143501df7ecdSRhyland Klein 14361db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 14375ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 14385ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 14395ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 14405ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 1441f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1442f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1443a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 144401df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 14455ebf2552SRhyland Klein }; 14465ebf2552SRhyland Klein 1447d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 14485ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 1449b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 14501743fa54SDmitry Osipenko .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR, 14517bf037d6SJon Hunter }; 14527bf037d6SJon Hunter 14534ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = { 14544ae12588SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 14554ae12588SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 14564ae12588SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 14574ae12588SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 14584ae12588SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 14594ae12588SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1460b960bc44SNicolin Chen .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 14614ae12588SThierry Reding .ops = &tegra114_sdhci_ops, 14624ae12588SThierry Reding }; 14634ae12588SThierry Reding 14644ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = { 14654ae12588SThierry Reding .pdata = &sdhci_tegra124_pdata, 1466b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34), 14671743fa54SDmitry Osipenko .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR, 14684ae12588SThierry Reding }; 14694ae12588SThierry Reding 14701070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = { 14710f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 14721070e83aSAapo Vienamo .read_w = tegra_sdhci_readw, 147338a284d9SAapo Vienamo .write_w = tegra210_sdhci_writew, 14741070e83aSAapo Vienamo .write_l = tegra_sdhci_writel, 14751070e83aSAapo Vienamo .set_clock = tegra_sdhci_set_clock, 1476b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 14771070e83aSAapo Vienamo .set_bus_width = sdhci_set_bus_width, 14781070e83aSAapo Vienamo .reset = tegra_sdhci_reset, 14791070e83aSAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 14801070e83aSAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch, 14811070e83aSAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 14825e958e4aSSowjanya Komatineni .set_timeout = tegra_sdhci_set_timeout, 14831070e83aSAapo Vienamo }; 14841070e83aSAapo Vienamo 1485b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 1486b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 1487b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 1488b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 1489a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1490a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1491a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 14921070e83aSAapo Vienamo .ops = &tegra210_sdhci_ops, 1493b5a84ecfSThierry Reding }; 1494b5a84ecfSThierry Reding 1495b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 1496b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 1497b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34), 1498d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1499d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 15003559d4a6SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 15013559d4a6SAapo Vienamo NVQUIRK_ENABLE_SDR50 | 15028048822bSSowjanya Komatineni NVQUIRK_ENABLE_SDR104 | 15038048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK, 1504ea8fc595SSowjanya Komatineni .min_tap_delay = 106, 1505ea8fc595SSowjanya Komatineni .max_tap_delay = 185, 1506b5a84ecfSThierry Reding }; 1507b5a84ecfSThierry Reding 150838a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = { 15090f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 151038a284d9SAapo Vienamo .read_w = tegra_sdhci_readw, 151138a284d9SAapo Vienamo .write_l = tegra_sdhci_writel, 151238a284d9SAapo Vienamo .set_clock = tegra_sdhci_set_clock, 1513b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 151438a284d9SAapo Vienamo .set_bus_width = sdhci_set_bus_width, 151538a284d9SAapo Vienamo .reset = tegra_sdhci_reset, 151638a284d9SAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 151738a284d9SAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch, 151838a284d9SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 15193c4019f9SSowjanya Komatineni .irq = sdhci_tegra_cqhci_irq, 15205e958e4aSSowjanya Komatineni .set_timeout = tegra_sdhci_set_timeout, 152138a284d9SAapo Vienamo }; 152238a284d9SAapo Vienamo 15234346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { 15244346b7c7SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 15254346b7c7SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 15264346b7c7SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 15274346b7c7SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 15284346b7c7SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1529b960bc44SNicolin Chen .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 153038a284d9SAapo Vienamo .ops = &tegra186_sdhci_ops, 15314346b7c7SThierry Reding }; 15324346b7c7SThierry Reding 15334346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = { 15344346b7c7SThierry Reding .pdata = &sdhci_tegra186_pdata, 1535b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(40), 1536d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1537d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 15382ad50051SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 15392ad50051SAapo Vienamo NVQUIRK_ENABLE_SDR50 | 1540c6e7ab90SSowjanya Komatineni NVQUIRK_ENABLE_SDR104 | 15418048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK | 1542c6e7ab90SSowjanya Komatineni NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING, 1543ea8fc595SSowjanya Komatineni .min_tap_delay = 84, 1544ea8fc595SSowjanya Komatineni .max_tap_delay = 136, 1545ea8fc595SSowjanya Komatineni }; 1546ea8fc595SSowjanya Komatineni 1547ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = { 1548ea8fc595SSowjanya Komatineni .pdata = &sdhci_tegra186_pdata, 1549b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(39), 1550ea8fc595SSowjanya Komatineni .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1551ea8fc595SSowjanya Komatineni NVQUIRK_HAS_PADCALIB | 1552ea8fc595SSowjanya Komatineni NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 1553ea8fc595SSowjanya Komatineni NVQUIRK_ENABLE_SDR50 | 15548048822bSSowjanya Komatineni NVQUIRK_ENABLE_SDR104 | 15558048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK, 1556ea8fc595SSowjanya Komatineni .min_tap_delay = 96, 1557ea8fc595SSowjanya Komatineni .max_tap_delay = 139, 15584346b7c7SThierry Reding }; 15594346b7c7SThierry Reding 1560498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 1561ea8fc595SSowjanya Komatineni { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 }, 15624346b7c7SThierry Reding { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, 1563b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 15644ae12588SThierry Reding { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, 15655ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 15663e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 15673e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 1568275173b2SGrant Likely {} 1569275173b2SGrant Likely }; 1570e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 1571275173b2SGrant Likely 15723c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host) 15733c4019f9SSowjanya Komatineni { 15743c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 15753c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 15763c4019f9SSowjanya Komatineni struct cqhci_host *cq_host; 15773c4019f9SSowjanya Komatineni bool dma64; 15783c4019f9SSowjanya Komatineni int ret; 15793c4019f9SSowjanya Komatineni 15803c4019f9SSowjanya Komatineni if (!tegra_host->enable_hwcq) 15813c4019f9SSowjanya Komatineni return sdhci_add_host(host); 15823c4019f9SSowjanya Komatineni 15833c4019f9SSowjanya Komatineni sdhci_enable_v4_mode(host); 15843c4019f9SSowjanya Komatineni 15853c4019f9SSowjanya Komatineni ret = sdhci_setup_host(host); 15863c4019f9SSowjanya Komatineni if (ret) 15873c4019f9SSowjanya Komatineni return ret; 15883c4019f9SSowjanya Komatineni 15893c4019f9SSowjanya Komatineni host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 15903c4019f9SSowjanya Komatineni 1591bac53336SJisheng Zhang cq_host = devm_kzalloc(mmc_dev(host->mmc), 15923c4019f9SSowjanya Komatineni sizeof(*cq_host), GFP_KERNEL); 15933c4019f9SSowjanya Komatineni if (!cq_host) { 15943c4019f9SSowjanya Komatineni ret = -ENOMEM; 15953c4019f9SSowjanya Komatineni goto cleanup; 15963c4019f9SSowjanya Komatineni } 15973c4019f9SSowjanya Komatineni 15983c4019f9SSowjanya Komatineni cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR; 15993c4019f9SSowjanya Komatineni cq_host->ops = &sdhci_tegra_cqhci_ops; 16003c4019f9SSowjanya Komatineni 16013c4019f9SSowjanya Komatineni dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 16023c4019f9SSowjanya Komatineni if (dma64) 16033c4019f9SSowjanya Komatineni cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 16043c4019f9SSowjanya Komatineni 16053c4019f9SSowjanya Komatineni ret = cqhci_init(cq_host, host->mmc, dma64); 16063c4019f9SSowjanya Komatineni if (ret) 16073c4019f9SSowjanya Komatineni goto cleanup; 16083c4019f9SSowjanya Komatineni 16093c4019f9SSowjanya Komatineni ret = __sdhci_add_host(host); 16103c4019f9SSowjanya Komatineni if (ret) 16113c4019f9SSowjanya Komatineni goto cleanup; 16123c4019f9SSowjanya Komatineni 16133c4019f9SSowjanya Komatineni return 0; 16143c4019f9SSowjanya Komatineni 16153c4019f9SSowjanya Komatineni cleanup: 16163c4019f9SSowjanya Komatineni sdhci_cleanup_host(host); 16173c4019f9SSowjanya Komatineni return ret; 16183c4019f9SSowjanya Komatineni } 16193c4019f9SSowjanya Komatineni 1620c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 162103d2bfc8SOlof Johansson { 16223e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 16233e44a1a7SStephen Warren struct sdhci_host *host; 162485d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 16253e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 162603d2bfc8SOlof Johansson struct clk *clk; 162703d2bfc8SOlof Johansson int rc; 162803d2bfc8SOlof Johansson 1629dfbaaec9SBean Huo soc_data = of_device_get_match_data(&pdev->dev); 1630dfbaaec9SBean Huo if (!soc_data) 1631b37f9d98SJoseph Lo return -EINVAL; 16323e44a1a7SStephen Warren 16330734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 163485d6509dSShawn Guo if (IS_ERR(host)) 163585d6509dSShawn Guo return PTR_ERR(host); 163685d6509dSShawn Guo pltfm_host = sdhci_priv(host); 163785d6509dSShawn Guo 16380734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host); 1639a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 1640e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 164186ac2f8bSAapo Vienamo tegra_host->pad_control_available = false; 16423e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 1643275173b2SGrant Likely 16441743fa54SDmitry Osipenko if (soc_data->nvquirks & NVQUIRK_HAS_ANDROID_GPT_SECTOR) 16451743fa54SDmitry Osipenko host->mmc->caps2 |= MMC_CAP2_ALT_GPT_TEGRA; 16461743fa54SDmitry Osipenko 164786ac2f8bSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) { 164886ac2f8bSAapo Vienamo rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); 164986ac2f8bSAapo Vienamo if (rc == 0) 165086ac2f8bSAapo Vienamo host->mmc_host_ops.start_signal_voltage_switch = 165186ac2f8bSAapo Vienamo sdhci_tegra_start_signal_voltage_switch; 165286ac2f8bSAapo Vienamo } 165386ac2f8bSAapo Vienamo 165461dad40eSAapo Vienamo /* Hook to periodically rerun pad calibration */ 165561dad40eSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 165661dad40eSAapo Vienamo host->mmc_host_ops.request = tegra_sdhci_request; 165761dad40eSAapo Vienamo 1658dfc9700cSAapo Vienamo host->mmc_host_ops.hs400_enhanced_strobe = 1659dfc9700cSAapo Vienamo tegra_sdhci_hs400_enhanced_strobe; 1660dfc9700cSAapo Vienamo 1661ea8fc595SSowjanya Komatineni if (!host->ops->platform_execute_tuning) 1662ea8fc595SSowjanya Komatineni host->mmc_host_ops.execute_tuning = 1663ea8fc595SSowjanya Komatineni tegra_sdhci_execute_hw_tuning; 1664ea8fc595SSowjanya Komatineni 16652391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 166647caa84fSSimon Baatz if (rc) 166747caa84fSSimon Baatz goto err_parse_dt; 16680e786102SStephen Warren 16697ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 1670c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 1671c3c2384cSLucas Stach 1672ff124c31SSowjanya Komatineni /* HW busy detection is supported, but R1B responses are required. */ 1673ff124c31SSowjanya Komatineni host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; 1674d2f8bfa4SUlf Hansson 167548f6daf1SAniruddha Rao /* GPIO CD can be set as a wakeup source */ 167648f6daf1SAniruddha Rao host->mmc->caps |= MMC_CAP_CD_WAKE; 167748f6daf1SAniruddha Rao 16783c4019f9SSowjanya Komatineni tegra_sdhci_parse_dt(host); 167985c0da17SAapo Vienamo 16802391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 16812391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 16822391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 16832391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 168485d6509dSShawn Guo goto err_power_req; 168503d2bfc8SOlof Johansson } 168603d2bfc8SOlof Johansson 16878048822bSSowjanya Komatineni /* 16888048822bSSowjanya Komatineni * Tegra210 has a separate SDMMC_LEGACY_TM clock used for host 16898048822bSSowjanya Komatineni * timeout clock and SW can choose TMCLK or SDCLK for hardware 16908048822bSSowjanya Komatineni * data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT of 16918048822bSSowjanya Komatineni * the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL. 16928048822bSSowjanya Komatineni * 16938048822bSSowjanya Komatineni * USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses 16948048822bSSowjanya Komatineni * 12Mhz TMCLK which is advertised in host capability register. 16958048822bSSowjanya Komatineni * With TMCLK of 12Mhz provides maximum data timeout period that can 16968048822bSSowjanya Komatineni * be achieved is 11s better than using SDCLK for data timeout. 16978048822bSSowjanya Komatineni * 16988048822bSSowjanya Komatineni * So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's 16998048822bSSowjanya Komatineni * supporting separate TMCLK. 17008048822bSSowjanya Komatineni */ 17018048822bSSowjanya Komatineni 17028048822bSSowjanya Komatineni if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) { 17038048822bSSowjanya Komatineni clk = devm_clk_get(&pdev->dev, "tmclk"); 17048048822bSSowjanya Komatineni if (IS_ERR(clk)) { 17058048822bSSowjanya Komatineni rc = PTR_ERR(clk); 17068048822bSSowjanya Komatineni if (rc == -EPROBE_DEFER) 17078048822bSSowjanya Komatineni goto err_power_req; 17088048822bSSowjanya Komatineni 17098048822bSSowjanya Komatineni dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc); 17108048822bSSowjanya Komatineni clk = NULL; 17118048822bSSowjanya Komatineni } 17128048822bSSowjanya Komatineni 17138048822bSSowjanya Komatineni clk_set_rate(clk, 12000000); 17148048822bSSowjanya Komatineni rc = clk_prepare_enable(clk); 17158048822bSSowjanya Komatineni if (rc) { 17168048822bSSowjanya Komatineni dev_err(&pdev->dev, 17178048822bSSowjanya Komatineni "failed to enable tmclk: %d\n", rc); 17188048822bSSowjanya Komatineni goto err_power_req; 17198048822bSSowjanya Komatineni } 17208048822bSSowjanya Komatineni 17218048822bSSowjanya Komatineni tegra_host->tmclk = clk; 17228048822bSSowjanya Komatineni } 17238048822bSSowjanya Komatineni 1724e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 172503d2bfc8SOlof Johansson if (IS_ERR(clk)) { 1726180a4665SKrzysztof Kozlowski rc = dev_err_probe(&pdev->dev, PTR_ERR(clk), 1727180a4665SKrzysztof Kozlowski "failed to get clock\n"); 172885d6509dSShawn Guo goto err_clk_get; 172903d2bfc8SOlof Johansson } 173003d2bfc8SOlof Johansson pltfm_host->clk = clk; 173103d2bfc8SOlof Johansson 17322cd6c49dSPhilipp Zabel tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, 17332cd6c49dSPhilipp Zabel "sdhci"); 173420567be9SThierry Reding if (IS_ERR(tegra_host->rst)) { 173520567be9SThierry Reding rc = PTR_ERR(tegra_host->rst); 173620567be9SThierry Reding dev_err(&pdev->dev, "failed to get reset control: %d\n", rc); 173720567be9SThierry Reding goto err_rst_get; 173820567be9SThierry Reding } 173920567be9SThierry Reding 1740d618978dSDmitry Osipenko rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); 174120567be9SThierry Reding if (rc) 174220567be9SThierry Reding goto err_rst_get; 174320567be9SThierry Reding 1744d618978dSDmitry Osipenko pm_runtime_enable(&pdev->dev); 1745d618978dSDmitry Osipenko rc = pm_runtime_resume_and_get(&pdev->dev); 1746d618978dSDmitry Osipenko if (rc) 1747d618978dSDmitry Osipenko goto err_pm_get; 1748d618978dSDmitry Osipenko 1749d618978dSDmitry Osipenko rc = reset_control_assert(tegra_host->rst); 1750d618978dSDmitry Osipenko if (rc) 1751d618978dSDmitry Osipenko goto err_rst_assert; 1752d618978dSDmitry Osipenko 175320567be9SThierry Reding usleep_range(2000, 4000); 175420567be9SThierry Reding 175520567be9SThierry Reding rc = reset_control_deassert(tegra_host->rst); 175620567be9SThierry Reding if (rc) 1757d618978dSDmitry Osipenko goto err_rst_assert; 175820567be9SThierry Reding 175920567be9SThierry Reding usleep_range(2000, 4000); 176020567be9SThierry Reding 17613c4019f9SSowjanya Komatineni rc = sdhci_tegra_add_host(host); 176285d6509dSShawn Guo if (rc) 176385d6509dSShawn Guo goto err_add_host; 176485d6509dSShawn Guo 176503d2bfc8SOlof Johansson return 0; 176603d2bfc8SOlof Johansson 176785d6509dSShawn Guo err_add_host: 176820567be9SThierry Reding reset_control_assert(tegra_host->rst); 1769d618978dSDmitry Osipenko err_rst_assert: 1770d618978dSDmitry Osipenko pm_runtime_put_sync_suspend(&pdev->dev); 1771d618978dSDmitry Osipenko err_pm_get: 1772d618978dSDmitry Osipenko pm_runtime_disable(&pdev->dev); 177320567be9SThierry Reding err_rst_get: 177485d6509dSShawn Guo err_clk_get: 17758048822bSSowjanya Komatineni clk_disable_unprepare(tegra_host->tmclk); 177685d6509dSShawn Guo err_power_req: 177747caa84fSSimon Baatz err_parse_dt: 177885d6509dSShawn Guo sdhci_pltfm_free(pdev); 177903d2bfc8SOlof Johansson return rc; 178003d2bfc8SOlof Johansson } 178103d2bfc8SOlof Johansson 178220567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev) 178320567be9SThierry Reding { 178420567be9SThierry Reding struct sdhci_host *host = platform_get_drvdata(pdev); 178520567be9SThierry Reding struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 178620567be9SThierry Reding struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 178720567be9SThierry Reding 178820567be9SThierry Reding sdhci_remove_host(host, 0); 178920567be9SThierry Reding 179020567be9SThierry Reding reset_control_assert(tegra_host->rst); 179120567be9SThierry Reding usleep_range(2000, 4000); 179220567be9SThierry Reding 1793d618978dSDmitry Osipenko pm_runtime_put_sync_suspend(&pdev->dev); 1794d618978dSDmitry Osipenko pm_runtime_force_suspend(&pdev->dev); 1795d618978dSDmitry Osipenko 1796d618978dSDmitry Osipenko clk_disable_unprepare(tegra_host->tmclk); 179720567be9SThierry Reding sdhci_pltfm_free(pdev); 179820567be9SThierry Reding 179920567be9SThierry Reding return 0; 180020567be9SThierry Reding } 180120567be9SThierry Reding 1802d618978dSDmitry Osipenko static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) 180371c733c4SSowjanya Komatineni { 180471c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev); 180571c733c4SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1806d618978dSDmitry Osipenko 1807d618978dSDmitry Osipenko clk_disable_unprepare(pltfm_host->clk); 1808d618978dSDmitry Osipenko 1809d618978dSDmitry Osipenko return 0; 1810d618978dSDmitry Osipenko } 1811d618978dSDmitry Osipenko 1812d618978dSDmitry Osipenko static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev) 1813d618978dSDmitry Osipenko { 1814d618978dSDmitry Osipenko struct sdhci_host *host = dev_get_drvdata(dev); 1815d618978dSDmitry Osipenko struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1816d618978dSDmitry Osipenko 1817d618978dSDmitry Osipenko return clk_prepare_enable(pltfm_host->clk); 1818d618978dSDmitry Osipenko } 1819d618978dSDmitry Osipenko 1820d618978dSDmitry Osipenko #ifdef CONFIG_PM_SLEEP 1821d618978dSDmitry Osipenko static int sdhci_tegra_suspend(struct device *dev) 1822d618978dSDmitry Osipenko { 1823d618978dSDmitry Osipenko struct sdhci_host *host = dev_get_drvdata(dev); 182471c733c4SSowjanya Komatineni int ret; 182571c733c4SSowjanya Komatineni 182671c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) { 182771c733c4SSowjanya Komatineni ret = cqhci_suspend(host->mmc); 182871c733c4SSowjanya Komatineni if (ret) 182971c733c4SSowjanya Komatineni return ret; 183071c733c4SSowjanya Komatineni } 183171c733c4SSowjanya Komatineni 183271c733c4SSowjanya Komatineni ret = sdhci_suspend_host(host); 183371c733c4SSowjanya Komatineni if (ret) { 183471c733c4SSowjanya Komatineni cqhci_resume(host->mmc); 183571c733c4SSowjanya Komatineni return ret; 183671c733c4SSowjanya Komatineni } 183771c733c4SSowjanya Komatineni 1838d618978dSDmitry Osipenko ret = pm_runtime_force_suspend(dev); 1839d618978dSDmitry Osipenko if (ret) { 1840d618978dSDmitry Osipenko sdhci_resume_host(host); 1841d618978dSDmitry Osipenko cqhci_resume(host->mmc); 1842d618978dSDmitry Osipenko return ret; 1843d618978dSDmitry Osipenko } 1844d618978dSDmitry Osipenko 184548f6daf1SAniruddha Rao return mmc_gpio_set_cd_wake(host->mmc, true); 184671c733c4SSowjanya Komatineni } 184771c733c4SSowjanya Komatineni 1848d618978dSDmitry Osipenko static int sdhci_tegra_resume(struct device *dev) 184971c733c4SSowjanya Komatineni { 185071c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev); 185171c733c4SSowjanya Komatineni int ret; 185271c733c4SSowjanya Komatineni 185348f6daf1SAniruddha Rao ret = mmc_gpio_set_cd_wake(host->mmc, false); 185448f6daf1SAniruddha Rao if (ret) 185548f6daf1SAniruddha Rao return ret; 185648f6daf1SAniruddha Rao 1857d618978dSDmitry Osipenko ret = pm_runtime_force_resume(dev); 185871c733c4SSowjanya Komatineni if (ret) 185971c733c4SSowjanya Komatineni return ret; 186071c733c4SSowjanya Komatineni 186171c733c4SSowjanya Komatineni ret = sdhci_resume_host(host); 186271c733c4SSowjanya Komatineni if (ret) 186371c733c4SSowjanya Komatineni goto disable_clk; 186471c733c4SSowjanya Komatineni 186571c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) { 186671c733c4SSowjanya Komatineni ret = cqhci_resume(host->mmc); 186771c733c4SSowjanya Komatineni if (ret) 186871c733c4SSowjanya Komatineni goto suspend_host; 186971c733c4SSowjanya Komatineni } 187071c733c4SSowjanya Komatineni 187171c733c4SSowjanya Komatineni return 0; 187271c733c4SSowjanya Komatineni 187371c733c4SSowjanya Komatineni suspend_host: 187471c733c4SSowjanya Komatineni sdhci_suspend_host(host); 187571c733c4SSowjanya Komatineni disable_clk: 1876d618978dSDmitry Osipenko pm_runtime_force_suspend(dev); 187771c733c4SSowjanya Komatineni return ret; 187871c733c4SSowjanya Komatineni } 187971c733c4SSowjanya Komatineni #endif 188071c733c4SSowjanya Komatineni 1881d618978dSDmitry Osipenko static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = { 1882d618978dSDmitry Osipenko SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, 1883d618978dSDmitry Osipenko NULL) 1884d618978dSDmitry Osipenko SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume) 1885d618978dSDmitry Osipenko }; 188671c733c4SSowjanya Komatineni 188785d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 188885d6509dSShawn Guo .driver = { 188985d6509dSShawn Guo .name = "sdhci-tegra", 189021b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 1891275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 189271c733c4SSowjanya Komatineni .pm = &sdhci_tegra_dev_pm_ops, 189385d6509dSShawn Guo }, 189485d6509dSShawn Guo .probe = sdhci_tegra_probe, 189520567be9SThierry Reding .remove = sdhci_tegra_remove, 189603d2bfc8SOlof Johansson }; 189703d2bfc8SOlof Johansson 1898d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 189985d6509dSShawn Guo 190085d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 190185d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 190285d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 1903