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> 1886ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h> 1920567be9SThierry Reding #include <linux/reset.h> 2003d2bfc8SOlof Johansson #include <linux/mmc/card.h> 2103d2bfc8SOlof Johansson #include <linux/mmc/host.h> 22c3c2384cSLucas Stach #include <linux/mmc/mmc.h> 230aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h> 242391b340SMylene JOSSERAND #include <linux/gpio/consumer.h> 2561dad40eSAapo Vienamo #include <linux/ktime.h> 2603d2bfc8SOlof Johansson 2703d2bfc8SOlof Johansson #include "sdhci-pltfm.h" 283c4019f9SSowjanya Komatineni #include "cqhci.h" 2903d2bfc8SOlof Johansson 30ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */ 3174cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 32c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 33c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 3441a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1f000000 3541a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24 36c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) 3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 3874cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 3974cd42bcSLucas Stach 40dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL 0x104 41dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE BIT(31) 42dfc9700cSAapo Vienamo 43f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES 0x10c 44f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK 0x00003f00 45f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT 8 46f5313aaaSAapo Vienamo 47ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 483145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 493145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 50ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 513145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 52ca5879d3SPavan Kunapuli 53bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG 0x1b0 54bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE BIT(31) 55bc5568bfSAapo Vienamo 56bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA 0x1bc 57bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE BIT(31) 58bc5568bfSAapo Vienamo 59d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 60d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 61ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK 0x03fc0000 62ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT 18 63ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK 0x00001fc0 64ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT 6 65ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK 0x000e000 66ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT 13 67ea8fc595SSowjanya Komatineni #define TRIES_128 2 68ea8fc595SSowjanya Komatineni #define TRIES_256 4 69ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK 0x7 70ea8fc595SSowjanya Komatineni 71ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0 0x1c4 72ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0 0x1C8 73ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1 0x1CC 74ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK 0xFF 75ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT 0x8 76ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE 32 77d4501d8eSAapo Vienamo 78e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 79e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START BIT(31) 80e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE BIT(29) 8151b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff 82e5c63d91SLucas Stach 839d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0 849d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f 859d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7 86212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31) 87de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK 0x07FFF000 889d548f11SAapo Vienamo 89e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec 90e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) 91e7c07148SAapo Vienamo 923e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) 933e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) 94ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) 957ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3) 967ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4) 977ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5) 98e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB BIT(6) 9986ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) 100d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) 101c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9) 1023e44a1a7SStephen Warren 1033c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ 1043c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 1053c4019f9SSowjanya Komatineni 1063e44a1a7SStephen Warren struct sdhci_tegra_soc_data { 1071db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata; 108b960bc44SNicolin Chen u64 dma_mask; 1093e44a1a7SStephen Warren u32 nvquirks; 110ea8fc595SSowjanya Komatineni u8 min_tap_delay; 111ea8fc595SSowjanya Komatineni u8 max_tap_delay; 1123e44a1a7SStephen Warren }; 1133e44a1a7SStephen Warren 11451b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */ 11551b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets { 11651b77c8eSAapo Vienamo u32 pull_up_3v3; 11751b77c8eSAapo Vienamo u32 pull_down_3v3; 11851b77c8eSAapo Vienamo u32 pull_up_3v3_timeout; 11951b77c8eSAapo Vienamo u32 pull_down_3v3_timeout; 12051b77c8eSAapo Vienamo u32 pull_up_1v8; 12151b77c8eSAapo Vienamo u32 pull_down_1v8; 12251b77c8eSAapo Vienamo u32 pull_up_1v8_timeout; 12351b77c8eSAapo Vienamo u32 pull_down_1v8_timeout; 12451b77c8eSAapo Vienamo u32 pull_up_sdr104; 12551b77c8eSAapo Vienamo u32 pull_down_sdr104; 12651b77c8eSAapo Vienamo u32 pull_up_hs400; 12751b77c8eSAapo Vienamo u32 pull_down_hs400; 12851b77c8eSAapo Vienamo }; 12951b77c8eSAapo Vienamo 1303e44a1a7SStephen Warren struct sdhci_tegra { 1313e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 1322391b340SMylene JOSSERAND struct gpio_desc *power_gpio; 133a8e326a9SLucas Stach bool ddr_signaling; 134e5c63d91SLucas Stach bool pad_calib_required; 13586ac2f8bSAapo Vienamo bool pad_control_available; 13620567be9SThierry Reding 13720567be9SThierry Reding struct reset_control *rst; 13886ac2f8bSAapo Vienamo struct pinctrl *pinctrl_sdmmc; 13986ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_3v3; 14086ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_1v8; 141de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_3v3_drv; 142de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_1v8_drv; 14351b77c8eSAapo Vienamo 14451b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets autocal_offsets; 14561dad40eSAapo Vienamo ktime_t last_calib; 14685c0da17SAapo Vienamo 14785c0da17SAapo Vienamo u32 default_tap; 14885c0da17SAapo Vienamo u32 default_trim; 149f5313aaaSAapo Vienamo u32 dqs_trim; 1503c4019f9SSowjanya Komatineni bool enable_hwcq; 151ea8fc595SSowjanya Komatineni unsigned long curr_clk_rate; 152ea8fc595SSowjanya Komatineni u8 tuned_tap_delay; 1533e44a1a7SStephen Warren }; 1543e44a1a7SStephen Warren 15503d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 15603d2bfc8SOlof Johansson { 1573e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1580734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1593e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1603e44a1a7SStephen Warren 1613e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && 1623e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) { 16303d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */ 16403d2bfc8SOlof Johansson return SDHCI_SPEC_200; 16503d2bfc8SOlof Johansson } 16603d2bfc8SOlof Johansson 16703d2bfc8SOlof Johansson return readw(host->ioaddr + reg); 16803d2bfc8SOlof Johansson } 16903d2bfc8SOlof Johansson 170352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 171352ee868SPavan Kunapuli { 172352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 173352ee868SPavan Kunapuli 174352ee868SPavan Kunapuli switch (reg) { 175352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE: 176352ee868SPavan Kunapuli /* 177352ee868SPavan Kunapuli * Postpone this write, we must do it together with a 178352ee868SPavan Kunapuli * command write that is down below. 179352ee868SPavan Kunapuli */ 180352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val; 181352ee868SPavan Kunapuli return; 182352ee868SPavan Kunapuli case SDHCI_COMMAND: 183352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow, 184352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE); 185352ee868SPavan Kunapuli return; 186352ee868SPavan Kunapuli } 187352ee868SPavan Kunapuli 188352ee868SPavan Kunapuli writew(val, host->ioaddr + reg); 189352ee868SPavan Kunapuli } 190352ee868SPavan Kunapuli 19103d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 19203d2bfc8SOlof Johansson { 1933e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1940734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1953e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1963e44a1a7SStephen Warren 19703d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so 19803d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software 19903d2bfc8SOlof Johansson * timers should take care of eventually detecting them. 20003d2bfc8SOlof Johansson */ 20103d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 20203d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 20303d2bfc8SOlof Johansson 20403d2bfc8SOlof Johansson writel(val, host->ioaddr + reg); 20503d2bfc8SOlof Johansson 2063e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && 2073e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) { 20803d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */ 20903d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 21003d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT) 21103d2bfc8SOlof Johansson gap_ctrl |= 0x8; 21203d2bfc8SOlof Johansson else 21303d2bfc8SOlof Johansson gap_ctrl &= ~0x8; 21403d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 21503d2bfc8SOlof Johansson } 21603d2bfc8SOlof Johansson } 21703d2bfc8SOlof Johansson 21838a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable) 21938a284d9SAapo Vienamo { 22038a284d9SAapo Vienamo bool status; 22138a284d9SAapo Vienamo u32 reg; 22238a284d9SAapo Vienamo 22338a284d9SAapo Vienamo reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 22438a284d9SAapo Vienamo status = !!(reg & SDHCI_CLOCK_CARD_EN); 22538a284d9SAapo Vienamo 22638a284d9SAapo Vienamo if (status == enable) 22738a284d9SAapo Vienamo return status; 22838a284d9SAapo Vienamo 22938a284d9SAapo Vienamo if (enable) 23038a284d9SAapo Vienamo reg |= SDHCI_CLOCK_CARD_EN; 23138a284d9SAapo Vienamo else 23238a284d9SAapo Vienamo reg &= ~SDHCI_CLOCK_CARD_EN; 23338a284d9SAapo Vienamo 23438a284d9SAapo Vienamo sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); 23538a284d9SAapo Vienamo 23638a284d9SAapo Vienamo return status; 23738a284d9SAapo Vienamo } 23838a284d9SAapo Vienamo 23938a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg) 24038a284d9SAapo Vienamo { 24138a284d9SAapo Vienamo bool is_tuning_cmd = 0; 24238a284d9SAapo Vienamo bool clk_enabled; 24338a284d9SAapo Vienamo u8 cmd; 24438a284d9SAapo Vienamo 24538a284d9SAapo Vienamo if (reg == SDHCI_COMMAND) { 24638a284d9SAapo Vienamo cmd = SDHCI_GET_CMD(val); 24738a284d9SAapo Vienamo is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK || 24838a284d9SAapo Vienamo cmd == MMC_SEND_TUNING_BLOCK_HS200; 24938a284d9SAapo Vienamo } 25038a284d9SAapo Vienamo 25138a284d9SAapo Vienamo if (is_tuning_cmd) 25238a284d9SAapo Vienamo clk_enabled = tegra_sdhci_configure_card_clk(host, 0); 25338a284d9SAapo Vienamo 25438a284d9SAapo Vienamo writew(val, host->ioaddr + reg); 25538a284d9SAapo Vienamo 25638a284d9SAapo Vienamo if (is_tuning_cmd) { 25738a284d9SAapo Vienamo udelay(1); 258ea8fc595SSowjanya Komatineni sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 25938a284d9SAapo Vienamo tegra_sdhci_configure_card_clk(host, clk_enabled); 26038a284d9SAapo Vienamo } 26138a284d9SAapo Vienamo } 26238a284d9SAapo Vienamo 2630f686ca9SDmitry Osipenko static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) 2640f686ca9SDmitry Osipenko { 2650f686ca9SDmitry Osipenko /* 2660f686ca9SDmitry Osipenko * Write-enable shall be assumed if GPIO is missing in a board's 2670f686ca9SDmitry Osipenko * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on 2680f686ca9SDmitry Osipenko * Tegra. 2690f686ca9SDmitry Osipenko */ 2700f686ca9SDmitry Osipenko return mmc_gpio_get_ro(host->mmc); 2710f686ca9SDmitry Osipenko } 2720f686ca9SDmitry Osipenko 27386ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host) 27486ac2f8bSAapo Vienamo { 27586ac2f8bSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 27686ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 27786ac2f8bSAapo Vienamo int has_1v8, has_3v3; 27886ac2f8bSAapo Vienamo 27986ac2f8bSAapo Vienamo /* 28086ac2f8bSAapo Vienamo * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad 28186ac2f8bSAapo Vienamo * voltage configuration in order to perform voltage switching. This 28286ac2f8bSAapo Vienamo * means that valid pinctrl info is required on SDHCI instances capable 28386ac2f8bSAapo Vienamo * of performing voltage switching. Whether or not an SDHCI instance is 28486ac2f8bSAapo Vienamo * capable of voltage switching is determined based on the regulator. 28586ac2f8bSAapo Vienamo */ 28686ac2f8bSAapo Vienamo 28786ac2f8bSAapo Vienamo if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL)) 28886ac2f8bSAapo Vienamo return true; 28986ac2f8bSAapo Vienamo 29086ac2f8bSAapo Vienamo if (IS_ERR(host->mmc->supply.vqmmc)) 29186ac2f8bSAapo Vienamo return false; 29286ac2f8bSAapo Vienamo 29386ac2f8bSAapo Vienamo has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 29486ac2f8bSAapo Vienamo 1700000, 1950000); 29586ac2f8bSAapo Vienamo 29686ac2f8bSAapo Vienamo has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc, 29786ac2f8bSAapo Vienamo 2700000, 3600000); 29886ac2f8bSAapo Vienamo 29986ac2f8bSAapo Vienamo if (has_1v8 == 1 && has_3v3 == 1) 30086ac2f8bSAapo Vienamo return tegra_host->pad_control_available; 30186ac2f8bSAapo Vienamo 30286ac2f8bSAapo Vienamo /* Fixed voltage, no pad control required. */ 30386ac2f8bSAapo Vienamo return true; 30486ac2f8bSAapo Vienamo } 30586ac2f8bSAapo Vienamo 306c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) 307c2c09678SAapo Vienamo { 308c2c09678SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 309c2c09678SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 310c2c09678SAapo Vienamo const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 311c2c09678SAapo Vienamo bool card_clk_enabled = false; 312c2c09678SAapo Vienamo u32 reg; 313c2c09678SAapo Vienamo 314c2c09678SAapo Vienamo /* 315c2c09678SAapo Vienamo * Touching the tap values is a bit tricky on some SoC generations. 316c2c09678SAapo Vienamo * The quirk enables a workaround for a glitch that sometimes occurs if 317c2c09678SAapo Vienamo * the tap values are changed. 318c2c09678SAapo Vienamo */ 319c2c09678SAapo Vienamo 320c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP) 321c2c09678SAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 322c2c09678SAapo Vienamo 323c2c09678SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 324c2c09678SAapo Vienamo reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; 325c2c09678SAapo Vienamo reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; 326c2c09678SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 327c2c09678SAapo Vienamo 328c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP && 329c2c09678SAapo Vienamo card_clk_enabled) { 330c2c09678SAapo Vienamo udelay(1); 331c2c09678SAapo Vienamo sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); 332c2c09678SAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 333c2c09678SAapo Vienamo } 334c2c09678SAapo Vienamo } 335c2c09678SAapo Vienamo 336dfc9700cSAapo Vienamo static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, 337dfc9700cSAapo Vienamo struct mmc_ios *ios) 338dfc9700cSAapo Vienamo { 339dfc9700cSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 340dfc9700cSAapo Vienamo u32 val; 341dfc9700cSAapo Vienamo 342dfc9700cSAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 343dfc9700cSAapo Vienamo 344dfc9700cSAapo Vienamo if (ios->enhanced_strobe) 345dfc9700cSAapo Vienamo val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 346dfc9700cSAapo Vienamo else 347dfc9700cSAapo Vienamo val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE; 348dfc9700cSAapo Vienamo 349dfc9700cSAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL); 350dfc9700cSAapo Vienamo 351dfc9700cSAapo Vienamo } 352dfc9700cSAapo Vienamo 35303231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) 354ca5879d3SPavan Kunapuli { 355ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3560734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 357ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 3589d548f11SAapo Vienamo u32 misc_ctrl, clk_ctrl, pad_ctrl; 359ca5879d3SPavan Kunapuli 36003231f9bSRussell King sdhci_reset(host, mask); 36103231f9bSRussell King 362ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL)) 363ca5879d3SPavan Kunapuli return; 364ca5879d3SPavan Kunapuli 365c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap); 366c2c09678SAapo Vienamo 3671b84def8SLucas Stach misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); 3684f6aa326SJon Hunter clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 3694f6aa326SJon Hunter 3704f6aa326SJon Hunter misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | 3714f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR50 | 3724f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_DDR50 | 3734f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR104); 3744f6aa326SJon Hunter 37541a0b8d7SAapo Vienamo clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK | 37641a0b8d7SAapo Vienamo SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE); 3774f6aa326SJon Hunter 37886ac2f8bSAapo Vienamo if (tegra_sdhci_is_pad_and_regulator_valid(host)) { 379ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */ 3803145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) 381ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; 3827ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */ 3837ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) 3847ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; 3857ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 3867ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; 3877ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) 3887ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; 3897ad2ed1dSLucas Stach if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) 390c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; 3914f6aa326SJon Hunter } 3924f6aa326SJon Hunter 39341a0b8d7SAapo Vienamo clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT; 39441a0b8d7SAapo Vienamo 3954f6aa326SJon Hunter sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 39674cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 39774cd42bcSLucas Stach 3989d548f11SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) { 3999d548f11SAapo Vienamo pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 4009d548f11SAapo Vienamo pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK; 4019d548f11SAapo Vienamo pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL; 4029d548f11SAapo Vienamo sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 4039d548f11SAapo Vienamo 404e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 4059d548f11SAapo Vienamo } 406e5c63d91SLucas Stach 407a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 408ca5879d3SPavan Kunapuli } 409ca5879d3SPavan Kunapuli 410212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable) 411212b0cf1SAapo Vienamo { 412212b0cf1SAapo Vienamo u32 val; 413212b0cf1SAapo Vienamo 414212b0cf1SAapo Vienamo /* 415212b0cf1SAapo Vienamo * Enable or disable the additional I/O pad used by the drive strength 416212b0cf1SAapo Vienamo * calibration process. 417212b0cf1SAapo Vienamo */ 418212b0cf1SAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 419212b0cf1SAapo Vienamo 420212b0cf1SAapo Vienamo if (enable) 421212b0cf1SAapo Vienamo val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 422212b0cf1SAapo Vienamo else 423212b0cf1SAapo Vienamo val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD; 424212b0cf1SAapo Vienamo 425212b0cf1SAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 426212b0cf1SAapo Vienamo 427212b0cf1SAapo Vienamo if (enable) 428212b0cf1SAapo Vienamo usleep_range(1, 2); 429212b0cf1SAapo Vienamo } 430212b0cf1SAapo Vienamo 43151b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host, 43251b77c8eSAapo Vienamo u16 pdpu) 43351b77c8eSAapo Vienamo { 43451b77c8eSAapo Vienamo u32 reg; 43551b77c8eSAapo Vienamo 43651b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 43751b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK; 43851b77c8eSAapo Vienamo reg |= pdpu; 43951b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 44051b77c8eSAapo Vienamo } 44151b77c8eSAapo Vienamo 442de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage, 443de25fa5aSSowjanya Komatineni bool state_drvupdn) 444de25fa5aSSowjanya Komatineni { 445de25fa5aSSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 446de25fa5aSSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 447de25fa5aSSowjanya Komatineni struct sdhci_tegra_autocal_offsets *offsets = 448de25fa5aSSowjanya Komatineni &tegra_host->autocal_offsets; 449de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_drvupdn = NULL; 450de25fa5aSSowjanya Komatineni int ret = 0; 451de25fa5aSSowjanya Komatineni u8 drvup = 0, drvdn = 0; 452de25fa5aSSowjanya Komatineni u32 reg; 453de25fa5aSSowjanya Komatineni 454de25fa5aSSowjanya Komatineni if (!state_drvupdn) { 455de25fa5aSSowjanya Komatineni /* PADS Drive Strength */ 456de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) { 457de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_1v8_drv) { 458de25fa5aSSowjanya Komatineni pinctrl_drvupdn = 459de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv; 460de25fa5aSSowjanya Komatineni } else { 461de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_1v8_timeout; 462de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_1v8_timeout; 463de25fa5aSSowjanya Komatineni } 464de25fa5aSSowjanya Komatineni } else { 465de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_3v3_drv) { 466de25fa5aSSowjanya Komatineni pinctrl_drvupdn = 467de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv; 468de25fa5aSSowjanya Komatineni } else { 469de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_3v3_timeout; 470de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_3v3_timeout; 471de25fa5aSSowjanya Komatineni } 472de25fa5aSSowjanya Komatineni } 473de25fa5aSSowjanya Komatineni 474de25fa5aSSowjanya Komatineni if (pinctrl_drvupdn != NULL) { 475de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 476de25fa5aSSowjanya Komatineni pinctrl_drvupdn); 477de25fa5aSSowjanya Komatineni if (ret < 0) 478de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 479de25fa5aSSowjanya Komatineni "failed pads drvupdn, ret: %d\n", ret); 480de25fa5aSSowjanya Komatineni } else if ((drvup) || (drvdn)) { 481de25fa5aSSowjanya Komatineni reg = sdhci_readl(host, 482de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 483de25fa5aSSowjanya Komatineni reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK; 484de25fa5aSSowjanya Komatineni reg |= (drvup << 20) | (drvdn << 12); 485de25fa5aSSowjanya Komatineni sdhci_writel(host, reg, 486de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL); 487de25fa5aSSowjanya Komatineni } 488de25fa5aSSowjanya Komatineni 489de25fa5aSSowjanya Komatineni } else { 490de25fa5aSSowjanya Komatineni /* Dual Voltage PADS Voltage selection */ 491de25fa5aSSowjanya Komatineni if (!tegra_host->pad_control_available) 492de25fa5aSSowjanya Komatineni return 0; 493de25fa5aSSowjanya Komatineni 494de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) { 495de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 496de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8); 497de25fa5aSSowjanya Komatineni if (ret < 0) 498de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 499de25fa5aSSowjanya Komatineni "setting 1.8V failed, ret: %d\n", ret); 500de25fa5aSSowjanya Komatineni } else { 501de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc, 502de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3); 503de25fa5aSSowjanya Komatineni if (ret < 0) 504de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 505de25fa5aSSowjanya Komatineni "setting 3.3V failed, ret: %d\n", ret); 506de25fa5aSSowjanya Komatineni } 507de25fa5aSSowjanya Komatineni } 508de25fa5aSSowjanya Komatineni 509de25fa5aSSowjanya Komatineni return ret; 510de25fa5aSSowjanya Komatineni } 511de25fa5aSSowjanya Komatineni 512e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) 513e5c63d91SLucas Stach { 51451b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 51551b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 51651b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets offsets = 51751b77c8eSAapo Vienamo tegra_host->autocal_offsets; 51851b77c8eSAapo Vienamo struct mmc_ios *ios = &host->mmc->ios; 519887bda8fSAapo Vienamo bool card_clk_enabled; 52051b77c8eSAapo Vienamo u16 pdpu; 521e7c07148SAapo Vienamo u32 reg; 522e7c07148SAapo Vienamo int ret; 523e5c63d91SLucas Stach 52451b77c8eSAapo Vienamo switch (ios->timing) { 52551b77c8eSAapo Vienamo case MMC_TIMING_UHS_SDR104: 52651b77c8eSAapo Vienamo pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104; 52751b77c8eSAapo Vienamo break; 52851b77c8eSAapo Vienamo case MMC_TIMING_MMC_HS400: 52951b77c8eSAapo Vienamo pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400; 53051b77c8eSAapo Vienamo break; 53151b77c8eSAapo Vienamo default: 53251b77c8eSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) 53351b77c8eSAapo Vienamo pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8; 53451b77c8eSAapo Vienamo else 53551b77c8eSAapo Vienamo pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3; 53651b77c8eSAapo Vienamo } 53751b77c8eSAapo Vienamo 538de25fa5aSSowjanya Komatineni /* Set initial offset before auto-calibration */ 53951b77c8eSAapo Vienamo tegra_sdhci_set_pad_autocal_offset(host, pdpu); 54051b77c8eSAapo Vienamo 541887bda8fSAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false); 542887bda8fSAapo Vienamo 543212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, true); 544212b0cf1SAapo Vienamo 545e7c07148SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 546e7c07148SAapo Vienamo reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; 547e7c07148SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 548e5c63d91SLucas Stach 549e7c07148SAapo Vienamo usleep_range(1, 2); 550e7c07148SAapo Vienamo /* 10 ms timeout */ 551e7c07148SAapo Vienamo ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS, 552e7c07148SAapo Vienamo reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE), 553e7c07148SAapo Vienamo 1000, 10000); 554e7c07148SAapo Vienamo 555212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, false); 556212b0cf1SAapo Vienamo 557887bda8fSAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled); 558887bda8fSAapo Vienamo 55951b77c8eSAapo Vienamo if (ret) { 560e7c07148SAapo Vienamo dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n"); 56151b77c8eSAapo Vienamo 562de25fa5aSSowjanya Komatineni /* Disable automatic cal and use fixed Drive Strengths */ 56351b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); 56451b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_ENABLE; 56551b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG); 56651b77c8eSAapo Vienamo 567de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false); 568de25fa5aSSowjanya Komatineni if (ret < 0) 569de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc), 570de25fa5aSSowjanya Komatineni "Setting drive strengths failed: %d\n", ret); 57151b77c8eSAapo Vienamo } 57251b77c8eSAapo Vienamo } 57351b77c8eSAapo Vienamo 57451b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host) 57551b77c8eSAapo Vienamo { 57651b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 57751b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 57851b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets *autocal = 57951b77c8eSAapo Vienamo &tegra_host->autocal_offsets; 58051b77c8eSAapo Vienamo int err; 58151b77c8eSAapo Vienamo 58251b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 58351b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3", 58451b77c8eSAapo Vienamo &autocal->pull_up_3v3); 58551b77c8eSAapo Vienamo if (err) 58651b77c8eSAapo Vienamo autocal->pull_up_3v3 = 0; 58751b77c8eSAapo Vienamo 58851b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 58951b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3", 59051b77c8eSAapo Vienamo &autocal->pull_down_3v3); 59151b77c8eSAapo Vienamo if (err) 59251b77c8eSAapo Vienamo autocal->pull_down_3v3 = 0; 59351b77c8eSAapo Vienamo 59451b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 59551b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8", 59651b77c8eSAapo Vienamo &autocal->pull_up_1v8); 59751b77c8eSAapo Vienamo if (err) 59851b77c8eSAapo Vienamo autocal->pull_up_1v8 = 0; 59951b77c8eSAapo Vienamo 60051b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 60151b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8", 60251b77c8eSAapo Vienamo &autocal->pull_down_1v8); 60351b77c8eSAapo Vienamo if (err) 60451b77c8eSAapo Vienamo autocal->pull_down_1v8 = 0; 60551b77c8eSAapo Vienamo 60651b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 60751b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3-timeout", 6085ccf7f55SSowjanya Komatineni &autocal->pull_up_3v3_timeout); 609de25fa5aSSowjanya Komatineni if (err) { 610de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 611de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL)) 612de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 613de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 61451b77c8eSAapo Vienamo autocal->pull_up_3v3_timeout = 0; 615de25fa5aSSowjanya Komatineni } 61651b77c8eSAapo Vienamo 61751b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 61851b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3-timeout", 6195ccf7f55SSowjanya Komatineni &autocal->pull_down_3v3_timeout); 620de25fa5aSSowjanya Komatineni if (err) { 621de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) && 622de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL)) 623de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n", 624de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 62551b77c8eSAapo Vienamo autocal->pull_down_3v3_timeout = 0; 626de25fa5aSSowjanya Komatineni } 62751b77c8eSAapo Vienamo 62851b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 62951b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8-timeout", 6305ccf7f55SSowjanya Komatineni &autocal->pull_up_1v8_timeout); 631de25fa5aSSowjanya Komatineni if (err) { 632de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 633de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL)) 634de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 635de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 63651b77c8eSAapo Vienamo autocal->pull_up_1v8_timeout = 0; 637de25fa5aSSowjanya Komatineni } 63851b77c8eSAapo Vienamo 63951b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 64051b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8-timeout", 6415ccf7f55SSowjanya Komatineni &autocal->pull_down_1v8_timeout); 642de25fa5aSSowjanya Komatineni if (err) { 643de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) && 644de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL)) 645de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n", 646de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc)); 64751b77c8eSAapo Vienamo autocal->pull_down_1v8_timeout = 0; 648de25fa5aSSowjanya Komatineni } 64951b77c8eSAapo Vienamo 65051b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 65151b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-sdr104", 65251b77c8eSAapo Vienamo &autocal->pull_up_sdr104); 65351b77c8eSAapo Vienamo if (err) 65451b77c8eSAapo Vienamo autocal->pull_up_sdr104 = autocal->pull_up_1v8; 65551b77c8eSAapo Vienamo 65651b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 65751b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-sdr104", 65851b77c8eSAapo Vienamo &autocal->pull_down_sdr104); 65951b77c8eSAapo Vienamo if (err) 66051b77c8eSAapo Vienamo autocal->pull_down_sdr104 = autocal->pull_down_1v8; 66151b77c8eSAapo Vienamo 66251b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 66351b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-hs400", 66451b77c8eSAapo Vienamo &autocal->pull_up_hs400); 66551b77c8eSAapo Vienamo if (err) 66651b77c8eSAapo Vienamo autocal->pull_up_hs400 = autocal->pull_up_1v8; 66751b77c8eSAapo Vienamo 66851b77c8eSAapo Vienamo err = device_property_read_u32(host->mmc->parent, 66951b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-hs400", 67051b77c8eSAapo Vienamo &autocal->pull_down_hs400); 67151b77c8eSAapo Vienamo if (err) 67251b77c8eSAapo Vienamo autocal->pull_down_hs400 = autocal->pull_down_1v8; 673e5c63d91SLucas Stach } 674e5c63d91SLucas Stach 67561dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) 67661dad40eSAapo Vienamo { 67761dad40eSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 67861dad40eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 67961dad40eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 68061dad40eSAapo Vienamo ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib); 68161dad40eSAapo Vienamo 68261dad40eSAapo Vienamo /* 100 ms calibration interval is specified in the TRM */ 68361dad40eSAapo Vienamo if (ktime_to_ms(since_calib) > 100) { 68461dad40eSAapo Vienamo tegra_sdhci_pad_autocalib(host); 68561dad40eSAapo Vienamo tegra_host->last_calib = ktime_get(); 68661dad40eSAapo Vienamo } 68761dad40eSAapo Vienamo 68861dad40eSAapo Vienamo sdhci_request(mmc, mrq); 68961dad40eSAapo Vienamo } 69061dad40eSAapo Vienamo 691f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host) 69285c0da17SAapo Vienamo { 69385c0da17SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 69485c0da17SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 69585c0da17SAapo Vienamo int err; 69685c0da17SAapo Vienamo 69785c0da17SAapo Vienamo err = device_property_read_u32(host->mmc->parent, "nvidia,default-tap", 69885c0da17SAapo Vienamo &tegra_host->default_tap); 69985c0da17SAapo Vienamo if (err) 70085c0da17SAapo Vienamo tegra_host->default_tap = 0; 70185c0da17SAapo Vienamo 70285c0da17SAapo Vienamo err = device_property_read_u32(host->mmc->parent, "nvidia,default-trim", 70385c0da17SAapo Vienamo &tegra_host->default_trim); 70485c0da17SAapo Vienamo if (err) 70585c0da17SAapo Vienamo tegra_host->default_trim = 0; 706f5313aaaSAapo Vienamo 707f5313aaaSAapo Vienamo err = device_property_read_u32(host->mmc->parent, "nvidia,dqs-trim", 708f5313aaaSAapo Vienamo &tegra_host->dqs_trim); 709f5313aaaSAapo Vienamo if (err) 710f5313aaaSAapo Vienamo tegra_host->dqs_trim = 0x11; 71185c0da17SAapo Vienamo } 71285c0da17SAapo Vienamo 7133c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host) 7143c4019f9SSowjanya Komatineni { 7153c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 7163c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 7173c4019f9SSowjanya Komatineni 7183c4019f9SSowjanya Komatineni if (device_property_read_bool(host->mmc->parent, "supports-cqe")) 7193c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = true; 7203c4019f9SSowjanya Komatineni else 7213c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = false; 7223c4019f9SSowjanya Komatineni 7233c4019f9SSowjanya Komatineni tegra_sdhci_parse_pad_autocal_dt(host); 7243c4019f9SSowjanya Komatineni tegra_sdhci_parse_tap_and_trim(host); 7253c4019f9SSowjanya Komatineni } 7263c4019f9SSowjanya Komatineni 727a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 728a8e326a9SLucas Stach { 729a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 7300734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 731a8e326a9SLucas Stach unsigned long host_clk; 732a8e326a9SLucas Stach 733a8e326a9SLucas Stach if (!clock) 7343491b690SLucas Stach return sdhci_set_clock(host, clock); 735a8e326a9SLucas Stach 73657d1654eSAapo Vienamo /* 73757d1654eSAapo Vienamo * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI 73857d1654eSAapo Vienamo * divider to be configured to divided the host clock by two. The SDHCI 73957d1654eSAapo Vienamo * clock divider is calculated as part of sdhci_set_clock() by 74057d1654eSAapo Vienamo * sdhci_calc_clk(). The divider is calculated from host->max_clk and 74157d1654eSAapo Vienamo * the requested clock rate. 74257d1654eSAapo Vienamo * 74357d1654eSAapo Vienamo * By setting the host->max_clk to clock * 2 the divider calculation 74457d1654eSAapo Vienamo * will always result in the correct value for DDR50/52 modes, 74557d1654eSAapo Vienamo * regardless of clock rate rounding, which may happen if the value 74657d1654eSAapo Vienamo * from clk_get_rate() is used. 74757d1654eSAapo Vienamo */ 748a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; 749a8e326a9SLucas Stach clk_set_rate(pltfm_host->clk, host_clk); 750ea8fc595SSowjanya Komatineni tegra_host->curr_clk_rate = host_clk; 75157d1654eSAapo Vienamo if (tegra_host->ddr_signaling) 75257d1654eSAapo Vienamo host->max_clk = host_clk; 75357d1654eSAapo Vienamo else 754a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk); 755a8e326a9SLucas Stach 756e5c63d91SLucas Stach sdhci_set_clock(host, clock); 757e5c63d91SLucas Stach 758e5c63d91SLucas Stach if (tegra_host->pad_calib_required) { 759e5c63d91SLucas Stach tegra_sdhci_pad_autocalib(host); 760e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 761e5c63d91SLucas Stach } 762a8e326a9SLucas Stach } 763a8e326a9SLucas Stach 76444350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) 76544350993SAapo Vienamo { 76644350993SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 76744350993SAapo Vienamo 76844350993SAapo Vienamo return clk_round_rate(pltfm_host->clk, UINT_MAX); 76944350993SAapo Vienamo } 77044350993SAapo Vienamo 771f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim) 772f5313aaaSAapo Vienamo { 773f5313aaaSAapo Vienamo u32 val; 774f5313aaaSAapo Vienamo 775f5313aaaSAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 776f5313aaaSAapo Vienamo val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK; 777f5313aaaSAapo Vienamo val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT; 778f5313aaaSAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES); 779f5313aaaSAapo Vienamo } 780f5313aaaSAapo Vienamo 781bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host) 782bc5568bfSAapo Vienamo { 783bc5568bfSAapo Vienamo u32 reg; 784bc5568bfSAapo Vienamo int err; 785bc5568bfSAapo Vienamo 786bc5568bfSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 787bc5568bfSAapo Vienamo reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE; 788bc5568bfSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG); 789bc5568bfSAapo Vienamo 790bc5568bfSAapo Vienamo /* 1 ms sleep, 5 ms timeout */ 791bc5568bfSAapo Vienamo err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA, 792bc5568bfSAapo Vienamo reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE), 793bc5568bfSAapo Vienamo 1000, 5000); 794bc5568bfSAapo Vienamo if (err) 795bc5568bfSAapo Vienamo dev_err(mmc_dev(host->mmc), 796bc5568bfSAapo Vienamo "HS400 delay line calibration timed out\n"); 797bc5568bfSAapo Vienamo } 798bc5568bfSAapo Vienamo 799ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up, 800ea8fc595SSowjanya Komatineni u8 thd_low, u8 fixed_tap) 801ea8fc595SSowjanya Komatineni { 802ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 803ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 804ea8fc595SSowjanya Komatineni u32 val, tun_status; 805ea8fc595SSowjanya Komatineni u8 word, bit, edge1, tap, window; 806ea8fc595SSowjanya Komatineni bool tap_result; 807ea8fc595SSowjanya Komatineni bool start_fail = false; 808ea8fc595SSowjanya Komatineni bool start_pass = false; 809ea8fc595SSowjanya Komatineni bool end_pass = false; 810ea8fc595SSowjanya Komatineni bool first_fail = false; 811ea8fc595SSowjanya Komatineni bool first_pass = false; 812ea8fc595SSowjanya Komatineni u8 start_pass_tap = 0; 813ea8fc595SSowjanya Komatineni u8 end_pass_tap = 0; 814ea8fc595SSowjanya Komatineni u8 first_fail_tap = 0; 815ea8fc595SSowjanya Komatineni u8 first_pass_tap = 0; 816ea8fc595SSowjanya Komatineni u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE; 817ea8fc595SSowjanya Komatineni 818ea8fc595SSowjanya Komatineni /* 819ea8fc595SSowjanya Komatineni * Read auto-tuned results and extract good valid passing window by 820ea8fc595SSowjanya Komatineni * filtering out un-wanted bubble/partial/merged windows. 821ea8fc595SSowjanya Komatineni */ 822ea8fc595SSowjanya Komatineni for (word = 0; word < total_tuning_words; word++) { 823ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 824ea8fc595SSowjanya Komatineni val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK; 825ea8fc595SSowjanya Komatineni val |= word; 826ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 827ea8fc595SSowjanya Komatineni tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0); 828ea8fc595SSowjanya Komatineni bit = 0; 829ea8fc595SSowjanya Komatineni while (bit < TUNING_WORD_BIT_SIZE) { 830ea8fc595SSowjanya Komatineni tap = word * TUNING_WORD_BIT_SIZE + bit; 831ea8fc595SSowjanya Komatineni tap_result = tun_status & (1 << bit); 832ea8fc595SSowjanya Komatineni if (!tap_result && !start_fail) { 833ea8fc595SSowjanya Komatineni start_fail = true; 834ea8fc595SSowjanya Komatineni if (!first_fail) { 835ea8fc595SSowjanya Komatineni first_fail_tap = tap; 836ea8fc595SSowjanya Komatineni first_fail = true; 837ea8fc595SSowjanya Komatineni } 838ea8fc595SSowjanya Komatineni 839ea8fc595SSowjanya Komatineni } else if (tap_result && start_fail && !start_pass) { 840ea8fc595SSowjanya Komatineni start_pass_tap = tap; 841ea8fc595SSowjanya Komatineni start_pass = true; 842ea8fc595SSowjanya Komatineni if (!first_pass) { 843ea8fc595SSowjanya Komatineni first_pass_tap = tap; 844ea8fc595SSowjanya Komatineni first_pass = true; 845ea8fc595SSowjanya Komatineni } 846ea8fc595SSowjanya Komatineni 847ea8fc595SSowjanya Komatineni } else if (!tap_result && start_fail && start_pass && 848ea8fc595SSowjanya Komatineni !end_pass) { 849ea8fc595SSowjanya Komatineni end_pass_tap = tap - 1; 850ea8fc595SSowjanya Komatineni end_pass = true; 851ea8fc595SSowjanya Komatineni } else if (tap_result && start_pass && start_fail && 852ea8fc595SSowjanya Komatineni end_pass) { 853ea8fc595SSowjanya Komatineni window = end_pass_tap - start_pass_tap; 854ea8fc595SSowjanya Komatineni /* discard merged window and bubble window */ 855ea8fc595SSowjanya Komatineni if (window >= thd_up || window < thd_low) { 856ea8fc595SSowjanya Komatineni start_pass_tap = tap; 857ea8fc595SSowjanya Komatineni end_pass = false; 858ea8fc595SSowjanya Komatineni } else { 859ea8fc595SSowjanya Komatineni /* set tap at middle of valid window */ 860ea8fc595SSowjanya Komatineni tap = start_pass_tap + window / 2; 861ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = tap; 862ea8fc595SSowjanya Komatineni return; 863ea8fc595SSowjanya Komatineni } 864ea8fc595SSowjanya Komatineni } 865ea8fc595SSowjanya Komatineni 866ea8fc595SSowjanya Komatineni bit++; 867ea8fc595SSowjanya Komatineni } 868ea8fc595SSowjanya Komatineni } 869ea8fc595SSowjanya Komatineni 870ea8fc595SSowjanya Komatineni if (!first_fail) { 871d96dc68eSDan Carpenter WARN(1, "no edge detected, continue with hw tuned delay.\n"); 872ea8fc595SSowjanya Komatineni } else if (first_pass) { 873ea8fc595SSowjanya Komatineni /* set tap location at fixed tap relative to the first edge */ 874ea8fc595SSowjanya Komatineni edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2; 875ea8fc595SSowjanya Komatineni if (edge1 - 1 > fixed_tap) 876ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 - fixed_tap; 877ea8fc595SSowjanya Komatineni else 878ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 + fixed_tap; 879ea8fc595SSowjanya Komatineni } 880ea8fc595SSowjanya Komatineni } 881ea8fc595SSowjanya Komatineni 882ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host) 883ea8fc595SSowjanya Komatineni { 884ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 885ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 886ea8fc595SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 887ea8fc595SSowjanya Komatineni u32 avg_tap_dly, val, min_tap_dly, max_tap_dly; 888ea8fc595SSowjanya Komatineni u8 fixed_tap, start_tap, end_tap, window_width; 889ea8fc595SSowjanya Komatineni u8 thdupper, thdlower; 890ea8fc595SSowjanya Komatineni u8 num_iter; 891ea8fc595SSowjanya Komatineni u32 clk_rate_mhz, period_ps, bestcase, worstcase; 892ea8fc595SSowjanya Komatineni 893ea8fc595SSowjanya Komatineni /* retain HW tuned tap to use incase if no correction is needed */ 894ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 895ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >> 896ea8fc595SSowjanya Komatineni SDHCI_CLOCK_CTRL_TAP_SHIFT; 897ea8fc595SSowjanya Komatineni if (soc_data->min_tap_delay && soc_data->max_tap_delay) { 898ea8fc595SSowjanya Komatineni min_tap_dly = soc_data->min_tap_delay; 899ea8fc595SSowjanya Komatineni max_tap_dly = soc_data->max_tap_delay; 900ea8fc595SSowjanya Komatineni clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC; 901ea8fc595SSowjanya Komatineni period_ps = USEC_PER_SEC / clk_rate_mhz; 902ea8fc595SSowjanya Komatineni bestcase = period_ps / min_tap_dly; 903ea8fc595SSowjanya Komatineni worstcase = period_ps / max_tap_dly; 904ea8fc595SSowjanya Komatineni /* 905ea8fc595SSowjanya Komatineni * Upper and Lower bound thresholds used to detect merged and 906ea8fc595SSowjanya Komatineni * bubble windows 907ea8fc595SSowjanya Komatineni */ 908ea8fc595SSowjanya Komatineni thdupper = (2 * worstcase + bestcase) / 2; 909ea8fc595SSowjanya Komatineni thdlower = worstcase / 4; 910ea8fc595SSowjanya Komatineni /* 911ea8fc595SSowjanya Komatineni * fixed tap is used when HW tuning result contains single edge 912ea8fc595SSowjanya Komatineni * and tap is set at fixed tap delay relative to the first edge 913ea8fc595SSowjanya Komatineni */ 914ea8fc595SSowjanya Komatineni avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly); 915ea8fc595SSowjanya Komatineni fixed_tap = avg_tap_dly / 2; 916ea8fc595SSowjanya Komatineni 917ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1); 918ea8fc595SSowjanya Komatineni start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 919ea8fc595SSowjanya Komatineni end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) & 920ea8fc595SSowjanya Komatineni SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK; 921ea8fc595SSowjanya Komatineni window_width = end_tap - start_tap; 922ea8fc595SSowjanya Komatineni num_iter = host->tuning_loop_count; 923ea8fc595SSowjanya Komatineni /* 924ea8fc595SSowjanya Komatineni * partial window includes edges of the tuning range. 925ea8fc595SSowjanya Komatineni * merged window includes more taps so window width is higher 926ea8fc595SSowjanya Komatineni * than upper threshold. 927ea8fc595SSowjanya Komatineni */ 928ea8fc595SSowjanya Komatineni if (start_tap == 0 || (end_tap == (num_iter - 1)) || 929ea8fc595SSowjanya Komatineni (end_tap == num_iter - 2) || window_width >= thdupper) { 930ea8fc595SSowjanya Komatineni pr_debug("%s: Apply tuning correction\n", 931ea8fc595SSowjanya Komatineni mmc_hostname(host->mmc)); 932ea8fc595SSowjanya Komatineni tegra_sdhci_tap_correction(host, thdupper, thdlower, 933ea8fc595SSowjanya Komatineni fixed_tap); 934ea8fc595SSowjanya Komatineni } 935ea8fc595SSowjanya Komatineni } 936ea8fc595SSowjanya Komatineni 937ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 938ea8fc595SSowjanya Komatineni } 939ea8fc595SSowjanya Komatineni 940ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode) 941ea8fc595SSowjanya Komatineni { 942ea8fc595SSowjanya Komatineni struct sdhci_host *host = mmc_priv(mmc); 943ea8fc595SSowjanya Komatineni int err; 944ea8fc595SSowjanya Komatineni 945ea8fc595SSowjanya Komatineni err = sdhci_execute_tuning(mmc, opcode); 946ea8fc595SSowjanya Komatineni if (!err && !host->tuning_err) 947ea8fc595SSowjanya Komatineni tegra_sdhci_post_tuning(host); 948ea8fc595SSowjanya Komatineni 949ea8fc595SSowjanya Komatineni return err; 950ea8fc595SSowjanya Komatineni } 951ea8fc595SSowjanya Komatineni 952c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, 953c2c09678SAapo Vienamo unsigned timing) 954c3c2384cSLucas Stach { 955d4501d8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 956d4501d8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 957c2c09678SAapo Vienamo bool set_default_tap = false; 958f5313aaaSAapo Vienamo bool set_dqs_trim = false; 959bc5568bfSAapo Vienamo bool do_hs400_dll_cal = false; 960ea8fc595SSowjanya Komatineni u8 iter = TRIES_256; 961ea8fc595SSowjanya Komatineni u32 val; 962c3c2384cSLucas Stach 96392cd1667SSowjanya Komatineni tegra_host->ddr_signaling = false; 964c2c09678SAapo Vienamo switch (timing) { 965c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR50: 966ea8fc595SSowjanya Komatineni break; 967c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR104: 968c2c09678SAapo Vienamo case MMC_TIMING_MMC_HS200: 969c2c09678SAapo Vienamo /* Don't set default tap on tunable modes. */ 970ea8fc595SSowjanya Komatineni iter = TRIES_128; 971c2c09678SAapo Vienamo break; 972f5313aaaSAapo Vienamo case MMC_TIMING_MMC_HS400: 973f5313aaaSAapo Vienamo set_dqs_trim = true; 974bc5568bfSAapo Vienamo do_hs400_dll_cal = true; 975ea8fc595SSowjanya Komatineni iter = TRIES_128; 976f5313aaaSAapo Vienamo break; 977c2c09678SAapo Vienamo case MMC_TIMING_MMC_DDR52: 978c2c09678SAapo Vienamo case MMC_TIMING_UHS_DDR50: 979c2c09678SAapo Vienamo tegra_host->ddr_signaling = true; 980c2c09678SAapo Vienamo set_default_tap = true; 981c2c09678SAapo Vienamo break; 982c2c09678SAapo Vienamo default: 983c2c09678SAapo Vienamo set_default_tap = true; 984c2c09678SAapo Vienamo break; 985d4501d8eSAapo Vienamo } 986c2c09678SAapo Vienamo 987ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0); 988ea8fc595SSowjanya Komatineni val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK | 989ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK | 990ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK); 991ea8fc595SSowjanya Komatineni val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT | 992ea8fc595SSowjanya Komatineni 0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT | 993ea8fc595SSowjanya Komatineni 1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT); 994ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0); 995ea8fc595SSowjanya Komatineni sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0); 996ea8fc595SSowjanya Komatineni 997ea8fc595SSowjanya Komatineni host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256; 998ea8fc595SSowjanya Komatineni 999c2c09678SAapo Vienamo sdhci_set_uhs_signaling(host, timing); 1000c2c09678SAapo Vienamo 1001c2c09678SAapo Vienamo tegra_sdhci_pad_autocalib(host); 1002c2c09678SAapo Vienamo 1003ea8fc595SSowjanya Komatineni if (tegra_host->tuned_tap_delay && !set_default_tap) 1004ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay); 1005ea8fc595SSowjanya Komatineni else 1006c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap); 1007f5313aaaSAapo Vienamo 1008f5313aaaSAapo Vienamo if (set_dqs_trim) 1009f5313aaaSAapo Vienamo tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim); 1010bc5568bfSAapo Vienamo 1011bc5568bfSAapo Vienamo if (do_hs400_dll_cal) 1012bc5568bfSAapo Vienamo tegra_sdhci_hs400_dll_cal(host); 1013c3c2384cSLucas Stach } 1014c3c2384cSLucas Stach 1015c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) 1016c3c2384cSLucas Stach { 1017c3c2384cSLucas Stach unsigned int min, max; 1018c3c2384cSLucas Stach 1019c3c2384cSLucas Stach /* 1020c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are 1021c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds, 1022c3c2384cSLucas Stach * according to the TRM. 1023c3c2384cSLucas Stach */ 1024c3c2384cSLucas Stach min = 10; 1025c3c2384cSLucas Stach while (min < 255) { 1026c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min); 1027c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL)) 1028c3c2384cSLucas Stach break; 1029c3c2384cSLucas Stach min++; 1030c3c2384cSLucas Stach } 1031c3c2384cSLucas Stach 1032c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */ 1033c3c2384cSLucas Stach max = min + 1; 1034c3c2384cSLucas Stach while (max < 255) { 1035c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max); 1036c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) { 1037c3c2384cSLucas Stach max--; 1038c3c2384cSLucas Stach break; 1039c3c2384cSLucas Stach } 1040c3c2384cSLucas Stach max++; 1041c3c2384cSLucas Stach } 1042c3c2384cSLucas Stach 1043c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */ 1044c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); 1045c3c2384cSLucas Stach 1046c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL); 1047c3c2384cSLucas Stach } 1048c3c2384cSLucas Stach 104986ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc, 105086ac2f8bSAapo Vienamo struct mmc_ios *ios) 105186ac2f8bSAapo Vienamo { 105286ac2f8bSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc); 105344babea2SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 105444babea2SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 105586ac2f8bSAapo Vienamo int ret = 0; 105686ac2f8bSAapo Vienamo 105786ac2f8bSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { 1058de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 105986ac2f8bSAapo Vienamo if (ret < 0) 106086ac2f8bSAapo Vienamo return ret; 106186ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 106286ac2f8bSAapo Vienamo } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { 106386ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios); 106486ac2f8bSAapo Vienamo if (ret < 0) 106586ac2f8bSAapo Vienamo return ret; 1066de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true); 106786ac2f8bSAapo Vienamo } 106886ac2f8bSAapo Vienamo 106944babea2SAapo Vienamo if (tegra_host->pad_calib_required) 107044babea2SAapo Vienamo tegra_sdhci_pad_autocalib(host); 107144babea2SAapo Vienamo 107286ac2f8bSAapo Vienamo return ret; 107386ac2f8bSAapo Vienamo } 107486ac2f8bSAapo Vienamo 107586ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev, 107686ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host) 107786ac2f8bSAapo Vienamo { 107886ac2f8bSAapo Vienamo tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev); 107986ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_sdmmc)) { 108086ac2f8bSAapo Vienamo dev_dbg(dev, "No pinctrl info, err: %ld\n", 108186ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_sdmmc)); 108286ac2f8bSAapo Vienamo return -1; 108386ac2f8bSAapo Vienamo } 108486ac2f8bSAapo Vienamo 1085de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state( 1086de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv"); 1087de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) { 1088de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV) 1089de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = NULL; 1090de25fa5aSSowjanya Komatineni } 1091de25fa5aSSowjanya Komatineni 1092de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state( 1093de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv"); 1094de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) { 1095de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV) 1096de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = NULL; 1097de25fa5aSSowjanya Komatineni } 1098de25fa5aSSowjanya Komatineni 109986ac2f8bSAapo Vienamo tegra_host->pinctrl_state_3v3 = 110086ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3"); 110186ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_3v3)) { 110286ac2f8bSAapo Vienamo dev_warn(dev, "Missing 3.3V pad state, err: %ld\n", 110386ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_state_3v3)); 110486ac2f8bSAapo Vienamo return -1; 110586ac2f8bSAapo Vienamo } 110686ac2f8bSAapo Vienamo 110786ac2f8bSAapo Vienamo tegra_host->pinctrl_state_1v8 = 110886ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8"); 110986ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_1v8)) { 111086ac2f8bSAapo Vienamo dev_warn(dev, "Missing 1.8V pad state, err: %ld\n", 1111e5378247SYueHaibing PTR_ERR(tegra_host->pinctrl_state_1v8)); 111286ac2f8bSAapo Vienamo return -1; 111386ac2f8bSAapo Vienamo } 111486ac2f8bSAapo Vienamo 111586ac2f8bSAapo Vienamo tegra_host->pad_control_available = true; 111686ac2f8bSAapo Vienamo 111786ac2f8bSAapo Vienamo return 0; 111886ac2f8bSAapo Vienamo } 111986ac2f8bSAapo Vienamo 1120e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host) 1121e5c63d91SLucas Stach { 1122e5c63d91SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1123e5c63d91SLucas Stach struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1124e5c63d91SLucas Stach const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1125e5c63d91SLucas Stach 1126e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 1127e5c63d91SLucas Stach tegra_host->pad_calib_required = true; 1128e5c63d91SLucas Stach } 1129e5c63d91SLucas Stach 1130b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg) 1131b7754428SSowjanya Komatineni { 1132b7754428SSowjanya Komatineni struct mmc_host *mmc = cq_host->mmc; 1133b7754428SSowjanya Komatineni u8 ctrl; 1134b7754428SSowjanya Komatineni ktime_t timeout; 1135b7754428SSowjanya Komatineni bool timed_out; 1136b7754428SSowjanya Komatineni 1137b7754428SSowjanya Komatineni /* 1138b7754428SSowjanya Komatineni * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to 1139b7754428SSowjanya Komatineni * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need 1140b7754428SSowjanya Komatineni * to be re-configured. 1141b7754428SSowjanya Komatineni * Tegra CQHCI/SDHCI prevents write access to block size register when 1142b7754428SSowjanya Komatineni * CQE is unhalted. So handling CQE resume sequence here to configure 1143b7754428SSowjanya Komatineni * SDHCI block registers prior to exiting CQE halt state. 1144b7754428SSowjanya Komatineni */ 1145b7754428SSowjanya Komatineni if (reg == CQHCI_CTL && !(val & CQHCI_HALT) && 1146b7754428SSowjanya Komatineni cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) { 1147b7754428SSowjanya Komatineni sdhci_cqe_enable(mmc); 1148b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1149b7754428SSowjanya Komatineni timeout = ktime_add_us(ktime_get(), 50); 1150b7754428SSowjanya Komatineni while (1) { 1151b7754428SSowjanya Komatineni timed_out = ktime_compare(ktime_get(), timeout) > 0; 1152b7754428SSowjanya Komatineni ctrl = cqhci_readl(cq_host, CQHCI_CTL); 1153b7754428SSowjanya Komatineni if (!(ctrl & CQHCI_HALT) || timed_out) 1154b7754428SSowjanya Komatineni break; 1155b7754428SSowjanya Komatineni } 1156b7754428SSowjanya Komatineni /* 1157b7754428SSowjanya Komatineni * CQE usually resumes very quick, but incase if Tegra CQE 1158b7754428SSowjanya Komatineni * doesn't resume retry unhalt. 1159b7754428SSowjanya Komatineni */ 1160b7754428SSowjanya Komatineni if (timed_out) 1161b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1162b7754428SSowjanya Komatineni } else { 1163b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg); 1164b7754428SSowjanya Komatineni } 1165b7754428SSowjanya Komatineni } 1166b7754428SSowjanya Komatineni 1167c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc, 1168c6e7ab90SSowjanya Komatineni struct mmc_request *mrq, u64 *data) 1169c6e7ab90SSowjanya Komatineni { 1170c6e7ab90SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc)); 1171c6e7ab90SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 1172c6e7ab90SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; 1173c6e7ab90SSowjanya Komatineni 1174c6e7ab90SSowjanya Komatineni if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING && 1175c6e7ab90SSowjanya Komatineni mrq->cmd->flags & MMC_RSP_R1B) 1176c6e7ab90SSowjanya Komatineni *data |= CQHCI_CMD_TIMING(1); 1177c6e7ab90SSowjanya Komatineni } 1178c6e7ab90SSowjanya Komatineni 11793c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc) 11803c4019f9SSowjanya Komatineni { 11813c4019f9SSowjanya Komatineni struct cqhci_host *cq_host = mmc->cqe_private; 1182b7754428SSowjanya Komatineni u32 val; 11833c4019f9SSowjanya Komatineni 11843c4019f9SSowjanya Komatineni /* 1185b7754428SSowjanya Komatineni * Tegra CQHCI/SDMMC design prevents write access to sdhci block size 1186b7754428SSowjanya Komatineni * register when CQE is enabled and unhalted. 1187b7754428SSowjanya Komatineni * CQHCI driver enables CQE prior to activation, so disable CQE before 1188b7754428SSowjanya Komatineni * programming block size in sdhci controller and enable it back. 11893c4019f9SSowjanya Komatineni */ 1190b7754428SSowjanya Komatineni if (!cq_host->activated) { 1191b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_CFG); 1192b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE) 1193b7754428SSowjanya Komatineni cqhci_writel(cq_host, (val & ~CQHCI_ENABLE), 1194b7754428SSowjanya Komatineni CQHCI_CFG); 11953c4019f9SSowjanya Komatineni sdhci_cqe_enable(mmc); 1196b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE) 1197b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_CFG); 1198b7754428SSowjanya Komatineni } 11993c4019f9SSowjanya Komatineni 1200b7754428SSowjanya Komatineni /* 1201b7754428SSowjanya Komatineni * CMD CRC errors are seen sometimes with some eMMC devices when status 1202b7754428SSowjanya Komatineni * command is sent during transfer of last data block which is the 1203b7754428SSowjanya Komatineni * default case as send status command block counter (CBC) is 1. 1204b7754428SSowjanya Komatineni * Recommended fix to set CBC to 0 allowing send status command only 1205b7754428SSowjanya Komatineni * when data lines are idle. 1206b7754428SSowjanya Komatineni */ 1207b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_SSC1); 1208b7754428SSowjanya Komatineni val &= ~CQHCI_SSC1_CBC_MASK; 1209b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_SSC1); 12103c4019f9SSowjanya Komatineni } 12113c4019f9SSowjanya Komatineni 12123c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc) 12133c4019f9SSowjanya Komatineni { 12143c4019f9SSowjanya Komatineni sdhci_dumpregs(mmc_priv(mmc)); 12153c4019f9SSowjanya Komatineni } 12163c4019f9SSowjanya Komatineni 12173c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask) 12183c4019f9SSowjanya Komatineni { 12193c4019f9SSowjanya Komatineni int cmd_error = 0; 12203c4019f9SSowjanya Komatineni int data_error = 0; 12213c4019f9SSowjanya Komatineni 12223c4019f9SSowjanya Komatineni if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 12233c4019f9SSowjanya Komatineni return intmask; 12243c4019f9SSowjanya Komatineni 12253c4019f9SSowjanya Komatineni cqhci_irq(host->mmc, intmask, cmd_error, data_error); 12263c4019f9SSowjanya Komatineni 12273c4019f9SSowjanya Komatineni return 0; 12283c4019f9SSowjanya Komatineni } 12293c4019f9SSowjanya Komatineni 12303c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { 1231b7754428SSowjanya Komatineni .write_l = tegra_cqhci_writel, 12323c4019f9SSowjanya Komatineni .enable = sdhci_tegra_cqe_enable, 12333c4019f9SSowjanya Komatineni .disable = sdhci_cqe_disable, 12343c4019f9SSowjanya Komatineni .dumpregs = sdhci_tegra_dumpregs, 1235c6e7ab90SSowjanya Komatineni .update_dcmd_desc = sdhci_tegra_update_dcmd_desc, 12363c4019f9SSowjanya Komatineni }; 12373c4019f9SSowjanya Komatineni 1238b960bc44SNicolin Chen static int tegra_sdhci_set_dma_mask(struct sdhci_host *host) 1239b960bc44SNicolin Chen { 1240b960bc44SNicolin Chen struct sdhci_pltfm_host *platform = sdhci_priv(host); 1241b960bc44SNicolin Chen struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform); 1242b960bc44SNicolin Chen const struct sdhci_tegra_soc_data *soc = tegra->soc_data; 1243b960bc44SNicolin Chen struct device *dev = mmc_dev(host->mmc); 1244b960bc44SNicolin Chen 1245b960bc44SNicolin Chen if (soc->dma_mask) 1246b960bc44SNicolin Chen return dma_set_mask_and_coherent(dev, soc->dma_mask); 1247b960bc44SNicolin Chen 1248b960bc44SNicolin Chen return 0; 1249b960bc44SNicolin Chen } 1250b960bc44SNicolin Chen 1251c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = { 12520f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 125385d6509dSShawn Guo .read_w = tegra_sdhci_readw, 125485d6509dSShawn Guo .write_l = tegra_sdhci_writel, 1255a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 1256b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 125714b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 125803231f9bSRussell King .reset = tegra_sdhci_reset, 1259c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 1260a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 1261e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 126244350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 126385d6509dSShawn Guo }; 126403d2bfc8SOlof Johansson 12651db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 126685d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 126785d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE | 126885d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT | 1269f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1270f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 127185d6509dSShawn Guo .ops = &tegra_sdhci_ops, 127285d6509dSShawn Guo }; 127385d6509dSShawn Guo 1274d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = { 12753e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata, 1276b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 12773e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | 12783e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET, 12793e44a1a7SStephen Warren }; 12803e44a1a7SStephen Warren 12811db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { 12823e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 12833e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 12843e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE | 12853e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT | 1286f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1287f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1288127407e3SStefan Agner .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 1289726df1d5SStefan Agner SDHCI_QUIRK2_BROKEN_HS200 | 1290726df1d5SStefan Agner /* 1291726df1d5SStefan Agner * Auto-CMD23 leads to "Got command interrupt 0x00010000 even 1292726df1d5SStefan Agner * though no command operation was in progress." 1293726df1d5SStefan Agner * 1294726df1d5SStefan Agner * The exact reason is unknown, as the same hardware seems 1295726df1d5SStefan Agner * to support Auto CMD23 on a downstream 3.1 kernel. 1296726df1d5SStefan Agner */ 1297726df1d5SStefan Agner SDHCI_QUIRK2_ACMD23_BROKEN, 12983e44a1a7SStephen Warren .ops = &tegra_sdhci_ops, 12993e44a1a7SStephen Warren }; 13003e44a1a7SStephen Warren 1301d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = { 13023e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata, 1303b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 13043145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | 13057ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 | 1306e5c63d91SLucas Stach NVQUIRK_ENABLE_SDR104 | 1307e5c63d91SLucas Stach NVQUIRK_HAS_PADCALIB, 13083e44a1a7SStephen Warren }; 13093e44a1a7SStephen Warren 131001df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = { 13110f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 131201df7ecdSRhyland Klein .read_w = tegra_sdhci_readw, 131301df7ecdSRhyland Klein .write_w = tegra_sdhci_writew, 131401df7ecdSRhyland Klein .write_l = tegra_sdhci_writel, 1315a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock, 1316b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 131714b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width, 131801df7ecdSRhyland Klein .reset = tegra_sdhci_reset, 1319c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning, 1320a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 1321e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch, 132244350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 132301df7ecdSRhyland Klein }; 132401df7ecdSRhyland Klein 13251db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 13265ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 13275ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 13285ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE | 13295ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT | 1330f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1331f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1332a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 133301df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops, 13345ebf2552SRhyland Klein }; 13355ebf2552SRhyland Klein 1336d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = { 13375ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata, 1338b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32), 13397bf037d6SJon Hunter }; 13407bf037d6SJon Hunter 13414ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = { 13424ae12588SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 13434ae12588SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 13444ae12588SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 13454ae12588SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 13464ae12588SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 13474ae12588SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1348b960bc44SNicolin Chen .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 13494ae12588SThierry Reding .ops = &tegra114_sdhci_ops, 13504ae12588SThierry Reding }; 13514ae12588SThierry Reding 13524ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = { 13534ae12588SThierry Reding .pdata = &sdhci_tegra124_pdata, 1354b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34), 13554ae12588SThierry Reding }; 13564ae12588SThierry Reding 13571070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = { 13580f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 13591070e83aSAapo Vienamo .read_w = tegra_sdhci_readw, 136038a284d9SAapo Vienamo .write_w = tegra210_sdhci_writew, 13611070e83aSAapo Vienamo .write_l = tegra_sdhci_writel, 13621070e83aSAapo Vienamo .set_clock = tegra_sdhci_set_clock, 1363b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 13641070e83aSAapo Vienamo .set_bus_width = sdhci_set_bus_width, 13651070e83aSAapo Vienamo .reset = tegra_sdhci_reset, 13661070e83aSAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 13671070e83aSAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch, 13681070e83aSAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 13691070e83aSAapo Vienamo }; 13701070e83aSAapo Vienamo 1371b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { 1372b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 1373b5a84ecfSThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 1374b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 1375b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 1376a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 1377a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1378a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 13791070e83aSAapo Vienamo .ops = &tegra210_sdhci_ops, 1380b5a84ecfSThierry Reding }; 1381b5a84ecfSThierry Reding 1382b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = { 1383b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata, 1384b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34), 1385d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1386d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 13873559d4a6SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 13883559d4a6SAapo Vienamo NVQUIRK_ENABLE_SDR50 | 13893559d4a6SAapo Vienamo NVQUIRK_ENABLE_SDR104, 1390ea8fc595SSowjanya Komatineni .min_tap_delay = 106, 1391ea8fc595SSowjanya Komatineni .max_tap_delay = 185, 1392b5a84ecfSThierry Reding }; 1393b5a84ecfSThierry Reding 139438a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = { 13950f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro, 139638a284d9SAapo Vienamo .read_w = tegra_sdhci_readw, 139738a284d9SAapo Vienamo .write_l = tegra_sdhci_writel, 139838a284d9SAapo Vienamo .set_clock = tegra_sdhci_set_clock, 1399b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask, 140038a284d9SAapo Vienamo .set_bus_width = sdhci_set_bus_width, 140138a284d9SAapo Vienamo .reset = tegra_sdhci_reset, 140238a284d9SAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 140338a284d9SAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch, 140438a284d9SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock, 14053c4019f9SSowjanya Komatineni .irq = sdhci_tegra_cqhci_irq, 140638a284d9SAapo Vienamo }; 140738a284d9SAapo Vienamo 14084346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { 14094346b7c7SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 14104346b7c7SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 14114346b7c7SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE | 14124346b7c7SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT | 14134346b7c7SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 14144346b7c7SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 1415b960bc44SNicolin Chen .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 141638a284d9SAapo Vienamo .ops = &tegra186_sdhci_ops, 14174346b7c7SThierry Reding }; 14184346b7c7SThierry Reding 14194346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = { 14204346b7c7SThierry Reding .pdata = &sdhci_tegra186_pdata, 1421b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(40), 1422d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1423d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB | 14242ad50051SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 14252ad50051SAapo Vienamo NVQUIRK_ENABLE_SDR50 | 1426c6e7ab90SSowjanya Komatineni NVQUIRK_ENABLE_SDR104 | 1427c6e7ab90SSowjanya Komatineni NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING, 1428ea8fc595SSowjanya Komatineni .min_tap_delay = 84, 1429ea8fc595SSowjanya Komatineni .max_tap_delay = 136, 1430ea8fc595SSowjanya Komatineni }; 1431ea8fc595SSowjanya Komatineni 1432ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = { 1433ea8fc595SSowjanya Komatineni .pdata = &sdhci_tegra186_pdata, 1434b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(39), 1435ea8fc595SSowjanya Komatineni .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | 1436ea8fc595SSowjanya Komatineni NVQUIRK_HAS_PADCALIB | 1437ea8fc595SSowjanya Komatineni NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | 1438ea8fc595SSowjanya Komatineni NVQUIRK_ENABLE_SDR50 | 1439ea8fc595SSowjanya Komatineni NVQUIRK_ENABLE_SDR104, 1440ea8fc595SSowjanya Komatineni .min_tap_delay = 96, 1441ea8fc595SSowjanya Komatineni .max_tap_delay = 139, 14424346b7c7SThierry Reding }; 14434346b7c7SThierry Reding 1444498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = { 1445ea8fc595SSowjanya Komatineni { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 }, 14464346b7c7SThierry Reding { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, 1447b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, 14484ae12588SThierry Reding { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, 14495ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, 14503e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, 14513e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, 1452275173b2SGrant Likely {} 1453275173b2SGrant Likely }; 1454e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); 1455275173b2SGrant Likely 14563c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host) 14573c4019f9SSowjanya Komatineni { 14583c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 14593c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 14603c4019f9SSowjanya Komatineni struct cqhci_host *cq_host; 14613c4019f9SSowjanya Komatineni bool dma64; 14623c4019f9SSowjanya Komatineni int ret; 14633c4019f9SSowjanya Komatineni 14643c4019f9SSowjanya Komatineni if (!tegra_host->enable_hwcq) 14653c4019f9SSowjanya Komatineni return sdhci_add_host(host); 14663c4019f9SSowjanya Komatineni 14673c4019f9SSowjanya Komatineni sdhci_enable_v4_mode(host); 14683c4019f9SSowjanya Komatineni 14693c4019f9SSowjanya Komatineni ret = sdhci_setup_host(host); 14703c4019f9SSowjanya Komatineni if (ret) 14713c4019f9SSowjanya Komatineni return ret; 14723c4019f9SSowjanya Komatineni 14733c4019f9SSowjanya Komatineni host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 14743c4019f9SSowjanya Komatineni 14753c4019f9SSowjanya Komatineni cq_host = devm_kzalloc(host->mmc->parent, 14763c4019f9SSowjanya Komatineni sizeof(*cq_host), GFP_KERNEL); 14773c4019f9SSowjanya Komatineni if (!cq_host) { 14783c4019f9SSowjanya Komatineni ret = -ENOMEM; 14793c4019f9SSowjanya Komatineni goto cleanup; 14803c4019f9SSowjanya Komatineni } 14813c4019f9SSowjanya Komatineni 14823c4019f9SSowjanya Komatineni cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR; 14833c4019f9SSowjanya Komatineni cq_host->ops = &sdhci_tegra_cqhci_ops; 14843c4019f9SSowjanya Komatineni 14853c4019f9SSowjanya Komatineni dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 14863c4019f9SSowjanya Komatineni if (dma64) 14873c4019f9SSowjanya Komatineni cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 14883c4019f9SSowjanya Komatineni 14893c4019f9SSowjanya Komatineni ret = cqhci_init(cq_host, host->mmc, dma64); 14903c4019f9SSowjanya Komatineni if (ret) 14913c4019f9SSowjanya Komatineni goto cleanup; 14923c4019f9SSowjanya Komatineni 14933c4019f9SSowjanya Komatineni ret = __sdhci_add_host(host); 14943c4019f9SSowjanya Komatineni if (ret) 14953c4019f9SSowjanya Komatineni goto cleanup; 14963c4019f9SSowjanya Komatineni 14973c4019f9SSowjanya Komatineni return 0; 14983c4019f9SSowjanya Komatineni 14993c4019f9SSowjanya Komatineni cleanup: 15003c4019f9SSowjanya Komatineni sdhci_cleanup_host(host); 15013c4019f9SSowjanya Komatineni return ret; 15023c4019f9SSowjanya Komatineni } 15033c4019f9SSowjanya Komatineni 1504c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev) 150503d2bfc8SOlof Johansson { 15063e44a1a7SStephen Warren const struct of_device_id *match; 15073e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data; 15083e44a1a7SStephen Warren struct sdhci_host *host; 150985d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host; 15103e44a1a7SStephen Warren struct sdhci_tegra *tegra_host; 151103d2bfc8SOlof Johansson struct clk *clk; 151203d2bfc8SOlof Johansson int rc; 151303d2bfc8SOlof Johansson 15143e44a1a7SStephen Warren match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); 1515b37f9d98SJoseph Lo if (!match) 1516b37f9d98SJoseph Lo return -EINVAL; 15173e44a1a7SStephen Warren soc_data = match->data; 15183e44a1a7SStephen Warren 15190734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); 152085d6509dSShawn Guo if (IS_ERR(host)) 152185d6509dSShawn Guo return PTR_ERR(host); 152285d6509dSShawn Guo pltfm_host = sdhci_priv(host); 152385d6509dSShawn Guo 15240734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host); 1525a8e326a9SLucas Stach tegra_host->ddr_signaling = false; 1526e5c63d91SLucas Stach tegra_host->pad_calib_required = false; 152786ac2f8bSAapo Vienamo tegra_host->pad_control_available = false; 15283e44a1a7SStephen Warren tegra_host->soc_data = soc_data; 1529275173b2SGrant Likely 153086ac2f8bSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) { 153186ac2f8bSAapo Vienamo rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host); 153286ac2f8bSAapo Vienamo if (rc == 0) 153386ac2f8bSAapo Vienamo host->mmc_host_ops.start_signal_voltage_switch = 153486ac2f8bSAapo Vienamo sdhci_tegra_start_signal_voltage_switch; 153586ac2f8bSAapo Vienamo } 153686ac2f8bSAapo Vienamo 153761dad40eSAapo Vienamo /* Hook to periodically rerun pad calibration */ 153861dad40eSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) 153961dad40eSAapo Vienamo host->mmc_host_ops.request = tegra_sdhci_request; 154061dad40eSAapo Vienamo 1541dfc9700cSAapo Vienamo host->mmc_host_ops.hs400_enhanced_strobe = 1542dfc9700cSAapo Vienamo tegra_sdhci_hs400_enhanced_strobe; 1543dfc9700cSAapo Vienamo 1544ea8fc595SSowjanya Komatineni if (!host->ops->platform_execute_tuning) 1545ea8fc595SSowjanya Komatineni host->mmc_host_ops.execute_tuning = 1546ea8fc595SSowjanya Komatineni tegra_sdhci_execute_hw_tuning; 1547ea8fc595SSowjanya Komatineni 15482391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc); 154947caa84fSSimon Baatz if (rc) 155047caa84fSSimon Baatz goto err_parse_dt; 15510e786102SStephen Warren 15527ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) 1553c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR; 1554c3c2384cSLucas Stach 15553c4019f9SSowjanya Komatineni tegra_sdhci_parse_dt(host); 155685c0da17SAapo Vienamo 15572391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 15582391b340SMylene JOSSERAND GPIOD_OUT_HIGH); 15592391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) { 15602391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio); 156185d6509dSShawn Guo goto err_power_req; 156203d2bfc8SOlof Johansson } 156303d2bfc8SOlof Johansson 1564e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL); 156503d2bfc8SOlof Johansson if (IS_ERR(clk)) { 156603d2bfc8SOlof Johansson rc = PTR_ERR(clk); 156700a9584eSThierry Reding 156800a9584eSThierry Reding if (rc != -EPROBE_DEFER) 156900a9584eSThierry Reding dev_err(&pdev->dev, "failed to get clock: %d\n", rc); 157000a9584eSThierry Reding 157185d6509dSShawn Guo goto err_clk_get; 157203d2bfc8SOlof Johansson } 15731e674bc6SPrashant Gaikwad clk_prepare_enable(clk); 157403d2bfc8SOlof Johansson pltfm_host->clk = clk; 157503d2bfc8SOlof Johansson 15762cd6c49dSPhilipp Zabel tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev, 15772cd6c49dSPhilipp Zabel "sdhci"); 157820567be9SThierry Reding if (IS_ERR(tegra_host->rst)) { 157920567be9SThierry Reding rc = PTR_ERR(tegra_host->rst); 158020567be9SThierry Reding dev_err(&pdev->dev, "failed to get reset control: %d\n", rc); 158120567be9SThierry Reding goto err_rst_get; 158220567be9SThierry Reding } 158320567be9SThierry Reding 158420567be9SThierry Reding rc = reset_control_assert(tegra_host->rst); 158520567be9SThierry Reding if (rc) 158620567be9SThierry Reding goto err_rst_get; 158720567be9SThierry Reding 158820567be9SThierry Reding usleep_range(2000, 4000); 158920567be9SThierry Reding 159020567be9SThierry Reding rc = reset_control_deassert(tegra_host->rst); 159120567be9SThierry Reding if (rc) 159220567be9SThierry Reding goto err_rst_get; 159320567be9SThierry Reding 159420567be9SThierry Reding usleep_range(2000, 4000); 159520567be9SThierry Reding 15963c4019f9SSowjanya Komatineni rc = sdhci_tegra_add_host(host); 159785d6509dSShawn Guo if (rc) 159885d6509dSShawn Guo goto err_add_host; 159985d6509dSShawn Guo 160003d2bfc8SOlof Johansson return 0; 160103d2bfc8SOlof Johansson 160285d6509dSShawn Guo err_add_host: 160320567be9SThierry Reding reset_control_assert(tegra_host->rst); 160420567be9SThierry Reding err_rst_get: 16051e674bc6SPrashant Gaikwad clk_disable_unprepare(pltfm_host->clk); 160685d6509dSShawn Guo err_clk_get: 160785d6509dSShawn Guo err_power_req: 160847caa84fSSimon Baatz err_parse_dt: 160985d6509dSShawn Guo sdhci_pltfm_free(pdev); 161003d2bfc8SOlof Johansson return rc; 161103d2bfc8SOlof Johansson } 161203d2bfc8SOlof Johansson 161320567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev) 161420567be9SThierry Reding { 161520567be9SThierry Reding struct sdhci_host *host = platform_get_drvdata(pdev); 161620567be9SThierry Reding struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 161720567be9SThierry Reding struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); 161820567be9SThierry Reding 161920567be9SThierry Reding sdhci_remove_host(host, 0); 162020567be9SThierry Reding 162120567be9SThierry Reding reset_control_assert(tegra_host->rst); 162220567be9SThierry Reding usleep_range(2000, 4000); 162320567be9SThierry Reding clk_disable_unprepare(pltfm_host->clk); 162420567be9SThierry Reding 162520567be9SThierry Reding sdhci_pltfm_free(pdev); 162620567be9SThierry Reding 162720567be9SThierry Reding return 0; 162820567be9SThierry Reding } 162920567be9SThierry Reding 163071c733c4SSowjanya Komatineni #ifdef CONFIG_PM_SLEEP 163171c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_suspend(struct device *dev) 163271c733c4SSowjanya Komatineni { 163371c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev); 163471c733c4SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 163571c733c4SSowjanya Komatineni int ret; 163671c733c4SSowjanya Komatineni 163771c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) { 163871c733c4SSowjanya Komatineni ret = cqhci_suspend(host->mmc); 163971c733c4SSowjanya Komatineni if (ret) 164071c733c4SSowjanya Komatineni return ret; 164171c733c4SSowjanya Komatineni } 164271c733c4SSowjanya Komatineni 164371c733c4SSowjanya Komatineni ret = sdhci_suspend_host(host); 164471c733c4SSowjanya Komatineni if (ret) { 164571c733c4SSowjanya Komatineni cqhci_resume(host->mmc); 164671c733c4SSowjanya Komatineni return ret; 164771c733c4SSowjanya Komatineni } 164871c733c4SSowjanya Komatineni 164971c733c4SSowjanya Komatineni clk_disable_unprepare(pltfm_host->clk); 165071c733c4SSowjanya Komatineni return 0; 165171c733c4SSowjanya Komatineni } 165271c733c4SSowjanya Komatineni 165371c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_resume(struct device *dev) 165471c733c4SSowjanya Komatineni { 165571c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev); 165671c733c4SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 165771c733c4SSowjanya Komatineni int ret; 165871c733c4SSowjanya Komatineni 165971c733c4SSowjanya Komatineni ret = clk_prepare_enable(pltfm_host->clk); 166071c733c4SSowjanya Komatineni if (ret) 166171c733c4SSowjanya Komatineni return ret; 166271c733c4SSowjanya Komatineni 166371c733c4SSowjanya Komatineni ret = sdhci_resume_host(host); 166471c733c4SSowjanya Komatineni if (ret) 166571c733c4SSowjanya Komatineni goto disable_clk; 166671c733c4SSowjanya Komatineni 166771c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) { 166871c733c4SSowjanya Komatineni ret = cqhci_resume(host->mmc); 166971c733c4SSowjanya Komatineni if (ret) 167071c733c4SSowjanya Komatineni goto suspend_host; 167171c733c4SSowjanya Komatineni } 167271c733c4SSowjanya Komatineni 167371c733c4SSowjanya Komatineni return 0; 167471c733c4SSowjanya Komatineni 167571c733c4SSowjanya Komatineni suspend_host: 167671c733c4SSowjanya Komatineni sdhci_suspend_host(host); 167771c733c4SSowjanya Komatineni disable_clk: 167871c733c4SSowjanya Komatineni clk_disable_unprepare(pltfm_host->clk); 167971c733c4SSowjanya Komatineni return ret; 168071c733c4SSowjanya Komatineni } 168171c733c4SSowjanya Komatineni #endif 168271c733c4SSowjanya Komatineni 168371c733c4SSowjanya Komatineni static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend, 168471c733c4SSowjanya Komatineni sdhci_tegra_resume); 168571c733c4SSowjanya Komatineni 168685d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = { 168785d6509dSShawn Guo .driver = { 168885d6509dSShawn Guo .name = "sdhci-tegra", 1689275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match, 169071c733c4SSowjanya Komatineni .pm = &sdhci_tegra_dev_pm_ops, 169185d6509dSShawn Guo }, 169285d6509dSShawn Guo .probe = sdhci_tegra_probe, 169320567be9SThierry Reding .remove = sdhci_tegra_remove, 169403d2bfc8SOlof Johansson }; 169503d2bfc8SOlof Johansson 1696d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver); 169785d6509dSShawn Guo 169885d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra"); 169985d6509dSShawn Guo MODULE_AUTHOR("Google, Inc."); 170085d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 1701