19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
203d2bfc8SOlof Johansson /*
303d2bfc8SOlof Johansson * Copyright (C) 2010 Google, Inc.
403d2bfc8SOlof Johansson */
503d2bfc8SOlof Johansson
603813c81SPrathamesh Shete #include <linux/bitfield.h>
707548a39SThierry Reding #include <linux/clk.h>
8e5c63d91SLucas Stach #include <linux/delay.h>
9b960bc44SNicolin Chen #include <linux/dma-mapping.h>
1003d2bfc8SOlof Johansson #include <linux/err.h>
1107548a39SThierry Reding #include <linux/gpio/consumer.h>
1203d2bfc8SOlof Johansson #include <linux/init.h>
1303d2bfc8SOlof Johansson #include <linux/io.h>
1403813c81SPrathamesh Shete #include <linux/iommu.h>
1507548a39SThierry Reding #include <linux/iopoll.h>
1607548a39SThierry Reding #include <linux/ktime.h>
1703d2bfc8SOlof Johansson #include <linux/mmc/card.h>
1803d2bfc8SOlof Johansson #include <linux/mmc/host.h>
19c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
200aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
2107548a39SThierry Reding #include <linux/module.h>
2207548a39SThierry Reding #include <linux/of.h>
2307548a39SThierry Reding #include <linux/pinctrl/consumer.h>
2407548a39SThierry Reding #include <linux/platform_device.h>
2507548a39SThierry Reding #include <linux/pm_opp.h>
2607548a39SThierry Reding #include <linux/pm_runtime.h>
2707548a39SThierry Reding #include <linux/regulator/consumer.h>
2807548a39SThierry Reding #include <linux/reset.h>
2903d2bfc8SOlof Johansson
30d618978dSDmitry Osipenko #include <soc/tegra/common.h>
31d618978dSDmitry Osipenko
3283607844SBrian Norris #include "sdhci-cqhci.h"
3303d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
343c4019f9SSowjanya Komatineni #include "cqhci.h"
3503d2bfc8SOlof Johansson
36ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3774cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
38c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000
39c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16
4041a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1f000000
4141a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24
42c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5)
4374cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3)
4474cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2)
4574cd42bcSLucas Stach
46dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL 0x104
47dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE BIT(31)
48dfc9700cSAapo Vienamo
49f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES 0x10c
50f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK 0x00003f00
51f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT 8
52f5313aaaSAapo Vienamo
53ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
545e958e4aSSowjanya Komatineni #define SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT BIT(0)
553145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8
563145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10
57ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
583145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
59ca5879d3SPavan Kunapuli
60bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG 0x1b0
61bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE BIT(31)
62bc5568bfSAapo Vienamo
63bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA 0x1bc
64bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE BIT(31)
65bc5568bfSAapo Vienamo
66d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0
67d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000
68ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK 0x03fc0000
69ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT 18
70ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK 0x00001fc0
71ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT 6
72ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK 0x000e000
73ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT 13
74ea8fc595SSowjanya Komatineni #define TRIES_128 2
75ea8fc595SSowjanya Komatineni #define TRIES_256 4
76ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK 0x7
77ea8fc595SSowjanya Komatineni
78ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0 0x1c4
79ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0 0x1C8
80ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1 0x1CC
81ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK 0xFF
82ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT 0x8
83ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE 32
84d4501d8eSAapo Vienamo
85e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
86e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START BIT(31)
87e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE BIT(29)
8851b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff
89e5c63d91SLucas Stach
909d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
919d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
929d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
93212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31)
94de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK 0x07FFF000
959d548f11SAapo Vienamo
96e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
97e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
98e7c07148SAapo Vienamo
9903813c81SPrathamesh Shete #define SDHCI_TEGRA_CIF2AXI_CTRL_0 0x1fc
10003813c81SPrathamesh Shete
1013e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
1023e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
103ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
1047ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50 BIT(3)
1057ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104 BIT(4)
1067ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50 BIT(5)
10747fad46bSSowjanya Komatineni /*
10847fad46bSSowjanya Komatineni * HAS_PADCALIB NVQUIRK is for SoC's supporting auto calibration of pads
10947fad46bSSowjanya Komatineni * drive strength.
11047fad46bSSowjanya Komatineni */
111e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB BIT(6)
11247fad46bSSowjanya Komatineni /*
11347fad46bSSowjanya Komatineni * NEEDS_PAD_CONTROL NVQUIRK is for SoC's having separate 3V3 and 1V8 pads.
11447fad46bSSowjanya Komatineni * 3V3/1V8 pad selection happens through pinctrl state selection depending
11547fad46bSSowjanya Komatineni * on the signaling mode.
11647fad46bSSowjanya Komatineni */
11786ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7)
118d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8)
119c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9)
1203e44a1a7SStephen Warren
1218048822bSSowjanya Komatineni /*
1228048822bSSowjanya Komatineni * NVQUIRK_HAS_TMCLK is for SoC's having separate timeout clock for Tegra
1238048822bSSowjanya Komatineni * SDMMC hardware data timeout.
1248048822bSSowjanya Komatineni */
1258048822bSSowjanya Komatineni #define NVQUIRK_HAS_TMCLK BIT(10)
1268048822bSSowjanya Komatineni
1271743fa54SDmitry Osipenko #define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11)
12803813c81SPrathamesh Shete #define NVQUIRK_PROGRAM_STREAMID BIT(12)
1291743fa54SDmitry Osipenko
1303c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
1313c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000
1323c4019f9SSowjanya Komatineni
1335ec6fa5aSAniruddha Tvs Rao #define SDHCI_TEGRA_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
1345ec6fa5aSAniruddha Tvs Rao SDHCI_TRNS_BLK_CNT_EN | \
1355ec6fa5aSAniruddha Tvs Rao SDHCI_TRNS_DMA)
1365ec6fa5aSAniruddha Tvs Rao
1373e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
1381db5eebfSLars-Peter Clausen const struct sdhci_pltfm_data *pdata;
139b960bc44SNicolin Chen u64 dma_mask;
1403e44a1a7SStephen Warren u32 nvquirks;
141ea8fc595SSowjanya Komatineni u8 min_tap_delay;
142ea8fc595SSowjanya Komatineni u8 max_tap_delay;
1433e44a1a7SStephen Warren };
1443e44a1a7SStephen Warren
14551b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
14651b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
14751b77c8eSAapo Vienamo u32 pull_up_3v3;
14851b77c8eSAapo Vienamo u32 pull_down_3v3;
14951b77c8eSAapo Vienamo u32 pull_up_3v3_timeout;
15051b77c8eSAapo Vienamo u32 pull_down_3v3_timeout;
15151b77c8eSAapo Vienamo u32 pull_up_1v8;
15251b77c8eSAapo Vienamo u32 pull_down_1v8;
15351b77c8eSAapo Vienamo u32 pull_up_1v8_timeout;
15451b77c8eSAapo Vienamo u32 pull_down_1v8_timeout;
15551b77c8eSAapo Vienamo u32 pull_up_sdr104;
15651b77c8eSAapo Vienamo u32 pull_down_sdr104;
15751b77c8eSAapo Vienamo u32 pull_up_hs400;
15851b77c8eSAapo Vienamo u32 pull_down_hs400;
15951b77c8eSAapo Vienamo };
16051b77c8eSAapo Vienamo
1613e44a1a7SStephen Warren struct sdhci_tegra {
1623e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data;
1632391b340SMylene JOSSERAND struct gpio_desc *power_gpio;
1648048822bSSowjanya Komatineni struct clk *tmclk;
165a8e326a9SLucas Stach bool ddr_signaling;
166e5c63d91SLucas Stach bool pad_calib_required;
16786ac2f8bSAapo Vienamo bool pad_control_available;
16820567be9SThierry Reding
16920567be9SThierry Reding struct reset_control *rst;
17086ac2f8bSAapo Vienamo struct pinctrl *pinctrl_sdmmc;
17186ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_3v3;
17286ac2f8bSAapo Vienamo struct pinctrl_state *pinctrl_state_1v8;
173de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_3v3_drv;
174de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_state_1v8_drv;
17551b77c8eSAapo Vienamo
17651b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets autocal_offsets;
17761dad40eSAapo Vienamo ktime_t last_calib;
17885c0da17SAapo Vienamo
17985c0da17SAapo Vienamo u32 default_tap;
18085c0da17SAapo Vienamo u32 default_trim;
181f5313aaaSAapo Vienamo u32 dqs_trim;
1823c4019f9SSowjanya Komatineni bool enable_hwcq;
183ea8fc595SSowjanya Komatineni unsigned long curr_clk_rate;
184ea8fc595SSowjanya Komatineni u8 tuned_tap_delay;
18503813c81SPrathamesh Shete u32 stream_id;
1863e44a1a7SStephen Warren };
1873e44a1a7SStephen Warren
tegra_sdhci_readw(struct sdhci_host * host,int reg)18803d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
18903d2bfc8SOlof Johansson {
1903e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1910734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1923e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1933e44a1a7SStephen Warren
1943e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1953e44a1a7SStephen Warren (reg == SDHCI_HOST_VERSION))) {
19603d2bfc8SOlof Johansson /* Erratum: Version register is invalid in HW. */
19703d2bfc8SOlof Johansson return SDHCI_SPEC_200;
19803d2bfc8SOlof Johansson }
19903d2bfc8SOlof Johansson
20003d2bfc8SOlof Johansson return readw(host->ioaddr + reg);
20103d2bfc8SOlof Johansson }
20203d2bfc8SOlof Johansson
tegra_sdhci_writew(struct sdhci_host * host,u16 val,int reg)203352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
204352ee868SPavan Kunapuli {
205352ee868SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
206352ee868SPavan Kunapuli
207352ee868SPavan Kunapuli switch (reg) {
208352ee868SPavan Kunapuli case SDHCI_TRANSFER_MODE:
209352ee868SPavan Kunapuli /*
210352ee868SPavan Kunapuli * Postpone this write, we must do it together with a
211352ee868SPavan Kunapuli * command write that is down below.
212352ee868SPavan Kunapuli */
213352ee868SPavan Kunapuli pltfm_host->xfer_mode_shadow = val;
214352ee868SPavan Kunapuli return;
215352ee868SPavan Kunapuli case SDHCI_COMMAND:
216352ee868SPavan Kunapuli writel((val << 16) | pltfm_host->xfer_mode_shadow,
217352ee868SPavan Kunapuli host->ioaddr + SDHCI_TRANSFER_MODE);
218352ee868SPavan Kunapuli return;
219352ee868SPavan Kunapuli }
220352ee868SPavan Kunapuli
221352ee868SPavan Kunapuli writew(val, host->ioaddr + reg);
222352ee868SPavan Kunapuli }
223352ee868SPavan Kunapuli
tegra_sdhci_writel(struct sdhci_host * host,u32 val,int reg)22403d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
22503d2bfc8SOlof Johansson {
2263e44a1a7SStephen Warren struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2270734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
2283e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
2293e44a1a7SStephen Warren
23003d2bfc8SOlof Johansson /* Seems like we're getting spurious timeout and crc errors, so
23103d2bfc8SOlof Johansson * disable signalling of them. In case of real errors software
23203d2bfc8SOlof Johansson * timers should take care of eventually detecting them.
23303d2bfc8SOlof Johansson */
23403d2bfc8SOlof Johansson if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
23503d2bfc8SOlof Johansson val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
23603d2bfc8SOlof Johansson
23703d2bfc8SOlof Johansson writel(val, host->ioaddr + reg);
23803d2bfc8SOlof Johansson
2393e44a1a7SStephen Warren if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
2403e44a1a7SStephen Warren (reg == SDHCI_INT_ENABLE))) {
24103d2bfc8SOlof Johansson /* Erratum: Must enable block gap interrupt detection */
24203d2bfc8SOlof Johansson u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
24303d2bfc8SOlof Johansson if (val & SDHCI_INT_CARD_INT)
24403d2bfc8SOlof Johansson gap_ctrl |= 0x8;
24503d2bfc8SOlof Johansson else
24603d2bfc8SOlof Johansson gap_ctrl &= ~0x8;
24703d2bfc8SOlof Johansson writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
24803d2bfc8SOlof Johansson }
24903d2bfc8SOlof Johansson }
25003d2bfc8SOlof Johansson
tegra_sdhci_configure_card_clk(struct sdhci_host * host,bool enable)25138a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
25238a284d9SAapo Vienamo {
25338a284d9SAapo Vienamo bool status;
25438a284d9SAapo Vienamo u32 reg;
25538a284d9SAapo Vienamo
25638a284d9SAapo Vienamo reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
25738a284d9SAapo Vienamo status = !!(reg & SDHCI_CLOCK_CARD_EN);
25838a284d9SAapo Vienamo
25938a284d9SAapo Vienamo if (status == enable)
26038a284d9SAapo Vienamo return status;
26138a284d9SAapo Vienamo
26238a284d9SAapo Vienamo if (enable)
26338a284d9SAapo Vienamo reg |= SDHCI_CLOCK_CARD_EN;
26438a284d9SAapo Vienamo else
26538a284d9SAapo Vienamo reg &= ~SDHCI_CLOCK_CARD_EN;
26638a284d9SAapo Vienamo
26738a284d9SAapo Vienamo sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
26838a284d9SAapo Vienamo
26938a284d9SAapo Vienamo return status;
27038a284d9SAapo Vienamo }
27138a284d9SAapo Vienamo
tegra210_sdhci_writew(struct sdhci_host * host,u16 val,int reg)27238a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
27338a284d9SAapo Vienamo {
27438a284d9SAapo Vienamo bool is_tuning_cmd = 0;
27538a284d9SAapo Vienamo bool clk_enabled;
27638a284d9SAapo Vienamo
277b98e7e8dSChanWoo Lee if (reg == SDHCI_COMMAND)
278b98e7e8dSChanWoo Lee is_tuning_cmd = mmc_op_tuning(SDHCI_GET_CMD(val));
27938a284d9SAapo Vienamo
28038a284d9SAapo Vienamo if (is_tuning_cmd)
28138a284d9SAapo Vienamo clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
28238a284d9SAapo Vienamo
28338a284d9SAapo Vienamo writew(val, host->ioaddr + reg);
28438a284d9SAapo Vienamo
28538a284d9SAapo Vienamo if (is_tuning_cmd) {
28638a284d9SAapo Vienamo udelay(1);
287ea8fc595SSowjanya Komatineni sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
28838a284d9SAapo Vienamo tegra_sdhci_configure_card_clk(host, clk_enabled);
28938a284d9SAapo Vienamo }
29038a284d9SAapo Vienamo }
29138a284d9SAapo Vienamo
tegra_sdhci_get_ro(struct sdhci_host * host)2920f686ca9SDmitry Osipenko static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
2930f686ca9SDmitry Osipenko {
2940f686ca9SDmitry Osipenko /*
2950f686ca9SDmitry Osipenko * Write-enable shall be assumed if GPIO is missing in a board's
2960f686ca9SDmitry Osipenko * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on
2970f686ca9SDmitry Osipenko * Tegra.
2980f686ca9SDmitry Osipenko */
2990f686ca9SDmitry Osipenko return mmc_gpio_get_ro(host->mmc);
3000f686ca9SDmitry Osipenko }
3010f686ca9SDmitry Osipenko
tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host * host)30286ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
30386ac2f8bSAapo Vienamo {
30486ac2f8bSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
30586ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
30686ac2f8bSAapo Vienamo int has_1v8, has_3v3;
30786ac2f8bSAapo Vienamo
30886ac2f8bSAapo Vienamo /*
30986ac2f8bSAapo Vienamo * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
31086ac2f8bSAapo Vienamo * voltage configuration in order to perform voltage switching. This
31186ac2f8bSAapo Vienamo * means that valid pinctrl info is required on SDHCI instances capable
31286ac2f8bSAapo Vienamo * of performing voltage switching. Whether or not an SDHCI instance is
31386ac2f8bSAapo Vienamo * capable of voltage switching is determined based on the regulator.
31486ac2f8bSAapo Vienamo */
31586ac2f8bSAapo Vienamo
31686ac2f8bSAapo Vienamo if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
31786ac2f8bSAapo Vienamo return true;
31886ac2f8bSAapo Vienamo
31986ac2f8bSAapo Vienamo if (IS_ERR(host->mmc->supply.vqmmc))
32086ac2f8bSAapo Vienamo return false;
32186ac2f8bSAapo Vienamo
32286ac2f8bSAapo Vienamo has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
32386ac2f8bSAapo Vienamo 1700000, 1950000);
32486ac2f8bSAapo Vienamo
32586ac2f8bSAapo Vienamo has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
32686ac2f8bSAapo Vienamo 2700000, 3600000);
32786ac2f8bSAapo Vienamo
32886ac2f8bSAapo Vienamo if (has_1v8 == 1 && has_3v3 == 1)
32986ac2f8bSAapo Vienamo return tegra_host->pad_control_available;
33086ac2f8bSAapo Vienamo
33186ac2f8bSAapo Vienamo /* Fixed voltage, no pad control required. */
33286ac2f8bSAapo Vienamo return true;
33386ac2f8bSAapo Vienamo }
33486ac2f8bSAapo Vienamo
tegra_sdhci_set_tap(struct sdhci_host * host,unsigned int tap)335c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
336c2c09678SAapo Vienamo {
337c2c09678SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
338c2c09678SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
339c2c09678SAapo Vienamo const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
340c2c09678SAapo Vienamo bool card_clk_enabled = false;
341c2c09678SAapo Vienamo u32 reg;
342c2c09678SAapo Vienamo
343c2c09678SAapo Vienamo /*
344c2c09678SAapo Vienamo * Touching the tap values is a bit tricky on some SoC generations.
345c2c09678SAapo Vienamo * The quirk enables a workaround for a glitch that sometimes occurs if
346c2c09678SAapo Vienamo * the tap values are changed.
347c2c09678SAapo Vienamo */
348c2c09678SAapo Vienamo
349c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
350c2c09678SAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
351c2c09678SAapo Vienamo
352c2c09678SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
353c2c09678SAapo Vienamo reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
354c2c09678SAapo Vienamo reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
355c2c09678SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
356c2c09678SAapo Vienamo
357c2c09678SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
358c2c09678SAapo Vienamo card_clk_enabled) {
359c2c09678SAapo Vienamo udelay(1);
360c2c09678SAapo Vienamo sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
361c2c09678SAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled);
362c2c09678SAapo Vienamo }
363c2c09678SAapo Vienamo }
364c2c09678SAapo Vienamo
tegra_sdhci_reset(struct sdhci_host * host,u8 mask)36503231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
366ca5879d3SPavan Kunapuli {
367ca5879d3SPavan Kunapuli struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
3680734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
369ca5879d3SPavan Kunapuli const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
3709d548f11SAapo Vienamo u32 misc_ctrl, clk_ctrl, pad_ctrl;
371ca5879d3SPavan Kunapuli
37283607844SBrian Norris sdhci_and_cqhci_reset(host, mask);
37303231f9bSRussell King
374ca5879d3SPavan Kunapuli if (!(mask & SDHCI_RESET_ALL))
375ca5879d3SPavan Kunapuli return;
376ca5879d3SPavan Kunapuli
377c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap);
378c2c09678SAapo Vienamo
3791b84def8SLucas Stach misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
3804f6aa326SJon Hunter clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
3814f6aa326SJon Hunter
3824f6aa326SJon Hunter misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
3834f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR50 |
3844f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_DDR50 |
3854f6aa326SJon Hunter SDHCI_MISC_CTRL_ENABLE_SDR104);
3864f6aa326SJon Hunter
38741a0b8d7SAapo Vienamo clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK |
38841a0b8d7SAapo Vienamo SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE);
3894f6aa326SJon Hunter
39086ac2f8bSAapo Vienamo if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
391ca5879d3SPavan Kunapuli /* Erratum: Enable SDHCI spec v3.00 support */
3923145351aSAndrew Bresticker if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
393ca5879d3SPavan Kunapuli misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
3947ad2ed1dSLucas Stach /* Advertise UHS modes as supported by host */
3957ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
3967ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
3977ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
3987ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
3997ad2ed1dSLucas Stach if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
4007ad2ed1dSLucas Stach misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
401f571389cSMichał Mirosław if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
402c3c2384cSLucas Stach clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
4034f6aa326SJon Hunter }
4044f6aa326SJon Hunter
40541a0b8d7SAapo Vienamo clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT;
40641a0b8d7SAapo Vienamo
4074f6aa326SJon Hunter sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
40874cd42bcSLucas Stach sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
40974cd42bcSLucas Stach
4109d548f11SAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
4119d548f11SAapo Vienamo pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4129d548f11SAapo Vienamo pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
4139d548f11SAapo Vienamo pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
4149d548f11SAapo Vienamo sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4159d548f11SAapo Vienamo
416e5c63d91SLucas Stach tegra_host->pad_calib_required = true;
4179d548f11SAapo Vienamo }
418e5c63d91SLucas Stach
419a8e326a9SLucas Stach tegra_host->ddr_signaling = false;
420ca5879d3SPavan Kunapuli }
421ca5879d3SPavan Kunapuli
tegra_sdhci_configure_cal_pad(struct sdhci_host * host,bool enable)422212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
423212b0cf1SAapo Vienamo {
424212b0cf1SAapo Vienamo u32 val;
425212b0cf1SAapo Vienamo
426212b0cf1SAapo Vienamo /*
427212b0cf1SAapo Vienamo * Enable or disable the additional I/O pad used by the drive strength
428212b0cf1SAapo Vienamo * calibration process.
429212b0cf1SAapo Vienamo */
430212b0cf1SAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
431212b0cf1SAapo Vienamo
432212b0cf1SAapo Vienamo if (enable)
433212b0cf1SAapo Vienamo val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
434212b0cf1SAapo Vienamo else
435212b0cf1SAapo Vienamo val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
436212b0cf1SAapo Vienamo
437212b0cf1SAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
438212b0cf1SAapo Vienamo
439212b0cf1SAapo Vienamo if (enable)
440212b0cf1SAapo Vienamo usleep_range(1, 2);
441212b0cf1SAapo Vienamo }
442212b0cf1SAapo Vienamo
tegra_sdhci_set_pad_autocal_offset(struct sdhci_host * host,u16 pdpu)44351b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
44451b77c8eSAapo Vienamo u16 pdpu)
44551b77c8eSAapo Vienamo {
44651b77c8eSAapo Vienamo u32 reg;
44751b77c8eSAapo Vienamo
44851b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
44951b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
45051b77c8eSAapo Vienamo reg |= pdpu;
45151b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
45251b77c8eSAapo Vienamo }
45351b77c8eSAapo Vienamo
tegra_sdhci_set_padctrl(struct sdhci_host * host,int voltage,bool state_drvupdn)454de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
455de25fa5aSSowjanya Komatineni bool state_drvupdn)
456de25fa5aSSowjanya Komatineni {
457de25fa5aSSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
458de25fa5aSSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
459de25fa5aSSowjanya Komatineni struct sdhci_tegra_autocal_offsets *offsets =
460de25fa5aSSowjanya Komatineni &tegra_host->autocal_offsets;
461de25fa5aSSowjanya Komatineni struct pinctrl_state *pinctrl_drvupdn = NULL;
462de25fa5aSSowjanya Komatineni int ret = 0;
463de25fa5aSSowjanya Komatineni u8 drvup = 0, drvdn = 0;
464de25fa5aSSowjanya Komatineni u32 reg;
465de25fa5aSSowjanya Komatineni
466de25fa5aSSowjanya Komatineni if (!state_drvupdn) {
467de25fa5aSSowjanya Komatineni /* PADS Drive Strength */
468de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) {
469de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_1v8_drv) {
470de25fa5aSSowjanya Komatineni pinctrl_drvupdn =
471de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv;
472de25fa5aSSowjanya Komatineni } else {
473de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_1v8_timeout;
474de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_1v8_timeout;
475de25fa5aSSowjanya Komatineni }
476de25fa5aSSowjanya Komatineni } else {
477de25fa5aSSowjanya Komatineni if (tegra_host->pinctrl_state_3v3_drv) {
478de25fa5aSSowjanya Komatineni pinctrl_drvupdn =
479de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv;
480de25fa5aSSowjanya Komatineni } else {
481de25fa5aSSowjanya Komatineni drvup = offsets->pull_up_3v3_timeout;
482de25fa5aSSowjanya Komatineni drvdn = offsets->pull_down_3v3_timeout;
483de25fa5aSSowjanya Komatineni }
484de25fa5aSSowjanya Komatineni }
485de25fa5aSSowjanya Komatineni
486de25fa5aSSowjanya Komatineni if (pinctrl_drvupdn != NULL) {
487de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
488de25fa5aSSowjanya Komatineni pinctrl_drvupdn);
489de25fa5aSSowjanya Komatineni if (ret < 0)
490de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc),
491de25fa5aSSowjanya Komatineni "failed pads drvupdn, ret: %d\n", ret);
492de25fa5aSSowjanya Komatineni } else if ((drvup) || (drvdn)) {
493de25fa5aSSowjanya Komatineni reg = sdhci_readl(host,
494de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
495de25fa5aSSowjanya Komatineni reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
496de25fa5aSSowjanya Komatineni reg |= (drvup << 20) | (drvdn << 12);
497de25fa5aSSowjanya Komatineni sdhci_writel(host, reg,
498de25fa5aSSowjanya Komatineni SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
499de25fa5aSSowjanya Komatineni }
500de25fa5aSSowjanya Komatineni
501de25fa5aSSowjanya Komatineni } else {
502de25fa5aSSowjanya Komatineni /* Dual Voltage PADS Voltage selection */
503de25fa5aSSowjanya Komatineni if (!tegra_host->pad_control_available)
504de25fa5aSSowjanya Komatineni return 0;
505de25fa5aSSowjanya Komatineni
506de25fa5aSSowjanya Komatineni if (voltage == MMC_SIGNAL_VOLTAGE_180) {
507de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
508de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8);
509de25fa5aSSowjanya Komatineni if (ret < 0)
510de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc),
511de25fa5aSSowjanya Komatineni "setting 1.8V failed, ret: %d\n", ret);
512de25fa5aSSowjanya Komatineni } else {
513de25fa5aSSowjanya Komatineni ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
514de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3);
515de25fa5aSSowjanya Komatineni if (ret < 0)
516de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc),
517de25fa5aSSowjanya Komatineni "setting 3.3V failed, ret: %d\n", ret);
518de25fa5aSSowjanya Komatineni }
519de25fa5aSSowjanya Komatineni }
520de25fa5aSSowjanya Komatineni
521de25fa5aSSowjanya Komatineni return ret;
522de25fa5aSSowjanya Komatineni }
523de25fa5aSSowjanya Komatineni
tegra_sdhci_pad_autocalib(struct sdhci_host * host)524e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
525e5c63d91SLucas Stach {
52651b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
52751b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
52851b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets offsets =
52951b77c8eSAapo Vienamo tegra_host->autocal_offsets;
53051b77c8eSAapo Vienamo struct mmc_ios *ios = &host->mmc->ios;
531887bda8fSAapo Vienamo bool card_clk_enabled;
53251b77c8eSAapo Vienamo u16 pdpu;
533e7c07148SAapo Vienamo u32 reg;
534e7c07148SAapo Vienamo int ret;
535e5c63d91SLucas Stach
53651b77c8eSAapo Vienamo switch (ios->timing) {
53751b77c8eSAapo Vienamo case MMC_TIMING_UHS_SDR104:
53851b77c8eSAapo Vienamo pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
53951b77c8eSAapo Vienamo break;
54051b77c8eSAapo Vienamo case MMC_TIMING_MMC_HS400:
54151b77c8eSAapo Vienamo pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
54251b77c8eSAapo Vienamo break;
54351b77c8eSAapo Vienamo default:
54451b77c8eSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
54551b77c8eSAapo Vienamo pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
54651b77c8eSAapo Vienamo else
54751b77c8eSAapo Vienamo pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
54851b77c8eSAapo Vienamo }
54951b77c8eSAapo Vienamo
550de25fa5aSSowjanya Komatineni /* Set initial offset before auto-calibration */
55151b77c8eSAapo Vienamo tegra_sdhci_set_pad_autocal_offset(host, pdpu);
55251b77c8eSAapo Vienamo
553887bda8fSAapo Vienamo card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
554887bda8fSAapo Vienamo
555212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, true);
556212b0cf1SAapo Vienamo
557e7c07148SAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
558e7c07148SAapo Vienamo reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
559e7c07148SAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
560e5c63d91SLucas Stach
561e7c07148SAapo Vienamo usleep_range(1, 2);
562e7c07148SAapo Vienamo /* 10 ms timeout */
563e7c07148SAapo Vienamo ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
564e7c07148SAapo Vienamo reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
565e7c07148SAapo Vienamo 1000, 10000);
566e7c07148SAapo Vienamo
567212b0cf1SAapo Vienamo tegra_sdhci_configure_cal_pad(host, false);
568212b0cf1SAapo Vienamo
569887bda8fSAapo Vienamo tegra_sdhci_configure_card_clk(host, card_clk_enabled);
570887bda8fSAapo Vienamo
57151b77c8eSAapo Vienamo if (ret) {
572e7c07148SAapo Vienamo dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
57351b77c8eSAapo Vienamo
574de25fa5aSSowjanya Komatineni /* Disable automatic cal and use fixed Drive Strengths */
57551b77c8eSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
57651b77c8eSAapo Vienamo reg &= ~SDHCI_AUTO_CAL_ENABLE;
57751b77c8eSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
57851b77c8eSAapo Vienamo
579de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
580de25fa5aSSowjanya Komatineni if (ret < 0)
581de25fa5aSSowjanya Komatineni dev_err(mmc_dev(host->mmc),
582de25fa5aSSowjanya Komatineni "Setting drive strengths failed: %d\n", ret);
58351b77c8eSAapo Vienamo }
58451b77c8eSAapo Vienamo }
58551b77c8eSAapo Vienamo
tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host * host)58651b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
58751b77c8eSAapo Vienamo {
58851b77c8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
58951b77c8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
59051b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets *autocal =
59151b77c8eSAapo Vienamo &tegra_host->autocal_offsets;
59251b77c8eSAapo Vienamo int err;
59351b77c8eSAapo Vienamo
594bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
59551b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3",
59651b77c8eSAapo Vienamo &autocal->pull_up_3v3);
59751b77c8eSAapo Vienamo if (err)
59851b77c8eSAapo Vienamo autocal->pull_up_3v3 = 0;
59951b77c8eSAapo Vienamo
600bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
60151b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3",
60251b77c8eSAapo Vienamo &autocal->pull_down_3v3);
60351b77c8eSAapo Vienamo if (err)
60451b77c8eSAapo Vienamo autocal->pull_down_3v3 = 0;
60551b77c8eSAapo Vienamo
606bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
60751b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8",
60851b77c8eSAapo Vienamo &autocal->pull_up_1v8);
60951b77c8eSAapo Vienamo if (err)
61051b77c8eSAapo Vienamo autocal->pull_up_1v8 = 0;
61151b77c8eSAapo Vienamo
612bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
61351b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8",
61451b77c8eSAapo Vienamo &autocal->pull_down_1v8);
61551b77c8eSAapo Vienamo if (err)
61651b77c8eSAapo Vienamo autocal->pull_down_1v8 = 0;
61751b77c8eSAapo Vienamo
618bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
619aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-up-offset-sdr104",
620aebbf577SSowjanya Komatineni &autocal->pull_up_sdr104);
621aebbf577SSowjanya Komatineni if (err)
622aebbf577SSowjanya Komatineni autocal->pull_up_sdr104 = autocal->pull_up_1v8;
623aebbf577SSowjanya Komatineni
624bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
625aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-down-offset-sdr104",
626aebbf577SSowjanya Komatineni &autocal->pull_down_sdr104);
627aebbf577SSowjanya Komatineni if (err)
628aebbf577SSowjanya Komatineni autocal->pull_down_sdr104 = autocal->pull_down_1v8;
629aebbf577SSowjanya Komatineni
630bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
631aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-up-offset-hs400",
632aebbf577SSowjanya Komatineni &autocal->pull_up_hs400);
633aebbf577SSowjanya Komatineni if (err)
634aebbf577SSowjanya Komatineni autocal->pull_up_hs400 = autocal->pull_up_1v8;
635aebbf577SSowjanya Komatineni
636bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
637aebbf577SSowjanya Komatineni "nvidia,pad-autocal-pull-down-offset-hs400",
638aebbf577SSowjanya Komatineni &autocal->pull_down_hs400);
639aebbf577SSowjanya Komatineni if (err)
640aebbf577SSowjanya Komatineni autocal->pull_down_hs400 = autocal->pull_down_1v8;
641aebbf577SSowjanya Komatineni
642aebbf577SSowjanya Komatineni /*
643aebbf577SSowjanya Komatineni * Different fail-safe drive strength values based on the signaling
644aebbf577SSowjanya Komatineni * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls.
645aebbf577SSowjanya Komatineni * So, avoid reading below device tree properties for SoCs that don't
646aebbf577SSowjanya Komatineni * have NVQUIRK_NEEDS_PAD_CONTROL.
647aebbf577SSowjanya Komatineni */
648aebbf577SSowjanya Komatineni if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
649aebbf577SSowjanya Komatineni return;
650aebbf577SSowjanya Komatineni
651bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
65251b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-3v3-timeout",
6535ccf7f55SSowjanya Komatineni &autocal->pull_up_3v3_timeout);
654de25fa5aSSowjanya Komatineni if (err) {
655de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
656de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL))
657de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
658de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc));
65951b77c8eSAapo Vienamo autocal->pull_up_3v3_timeout = 0;
660de25fa5aSSowjanya Komatineni }
66151b77c8eSAapo Vienamo
662bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
66351b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-3v3-timeout",
6645ccf7f55SSowjanya Komatineni &autocal->pull_down_3v3_timeout);
665de25fa5aSSowjanya Komatineni if (err) {
666de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
667de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_3v3_drv == NULL))
668de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
669de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc));
67051b77c8eSAapo Vienamo autocal->pull_down_3v3_timeout = 0;
671de25fa5aSSowjanya Komatineni }
67251b77c8eSAapo Vienamo
673bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
67451b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-up-offset-1v8-timeout",
6755ccf7f55SSowjanya Komatineni &autocal->pull_up_1v8_timeout);
676de25fa5aSSowjanya Komatineni if (err) {
677de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
678de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL))
679de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
680de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc));
68151b77c8eSAapo Vienamo autocal->pull_up_1v8_timeout = 0;
682de25fa5aSSowjanya Komatineni }
68351b77c8eSAapo Vienamo
684bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc),
68551b77c8eSAapo Vienamo "nvidia,pad-autocal-pull-down-offset-1v8-timeout",
6865ccf7f55SSowjanya Komatineni &autocal->pull_down_1v8_timeout);
687de25fa5aSSowjanya Komatineni if (err) {
688de25fa5aSSowjanya Komatineni if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
689de25fa5aSSowjanya Komatineni (tegra_host->pinctrl_state_1v8_drv == NULL))
690de25fa5aSSowjanya Komatineni pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
691de25fa5aSSowjanya Komatineni mmc_hostname(host->mmc));
69251b77c8eSAapo Vienamo autocal->pull_down_1v8_timeout = 0;
693de25fa5aSSowjanya Komatineni }
694e5c63d91SLucas Stach }
695e5c63d91SLucas Stach
tegra_sdhci_request(struct mmc_host * mmc,struct mmc_request * mrq)69661dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
69761dad40eSAapo Vienamo {
69861dad40eSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc);
69961dad40eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
70061dad40eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
70161dad40eSAapo Vienamo ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib);
70261dad40eSAapo Vienamo
70361dad40eSAapo Vienamo /* 100 ms calibration interval is specified in the TRM */
70461dad40eSAapo Vienamo if (ktime_to_ms(since_calib) > 100) {
70561dad40eSAapo Vienamo tegra_sdhci_pad_autocalib(host);
70661dad40eSAapo Vienamo tegra_host->last_calib = ktime_get();
70761dad40eSAapo Vienamo }
70861dad40eSAapo Vienamo
70961dad40eSAapo Vienamo sdhci_request(mmc, mrq);
71061dad40eSAapo Vienamo }
71161dad40eSAapo Vienamo
tegra_sdhci_parse_tap_and_trim(struct sdhci_host * host)712f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
71385c0da17SAapo Vienamo {
71485c0da17SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
71585c0da17SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
71685c0da17SAapo Vienamo int err;
71785c0da17SAapo Vienamo
718bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-tap",
71985c0da17SAapo Vienamo &tegra_host->default_tap);
72085c0da17SAapo Vienamo if (err)
72185c0da17SAapo Vienamo tegra_host->default_tap = 0;
72285c0da17SAapo Vienamo
723bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,default-trim",
72485c0da17SAapo Vienamo &tegra_host->default_trim);
72585c0da17SAapo Vienamo if (err)
72685c0da17SAapo Vienamo tegra_host->default_trim = 0;
727f5313aaaSAapo Vienamo
728bac53336SJisheng Zhang err = device_property_read_u32(mmc_dev(host->mmc), "nvidia,dqs-trim",
729f5313aaaSAapo Vienamo &tegra_host->dqs_trim);
730f5313aaaSAapo Vienamo if (err)
731f5313aaaSAapo Vienamo tegra_host->dqs_trim = 0x11;
73285c0da17SAapo Vienamo }
73385c0da17SAapo Vienamo
tegra_sdhci_parse_dt(struct sdhci_host * host)7343c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host)
7353c4019f9SSowjanya Komatineni {
7363c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7373c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
7383c4019f9SSowjanya Komatineni
739bac53336SJisheng Zhang if (device_property_read_bool(mmc_dev(host->mmc), "supports-cqe"))
7403c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = true;
7413c4019f9SSowjanya Komatineni else
7423c4019f9SSowjanya Komatineni tegra_host->enable_hwcq = false;
7433c4019f9SSowjanya Komatineni
7443c4019f9SSowjanya Komatineni tegra_sdhci_parse_pad_autocal_dt(host);
7453c4019f9SSowjanya Komatineni tegra_sdhci_parse_tap_and_trim(host);
7463c4019f9SSowjanya Komatineni }
7473c4019f9SSowjanya Komatineni
tegra_sdhci_set_clock(struct sdhci_host * host,unsigned int clock)748a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
749a8e326a9SLucas Stach {
750a8e326a9SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7510734e79cSJisheng Zhang struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
752d618978dSDmitry Osipenko struct device *dev = mmc_dev(host->mmc);
753a8e326a9SLucas Stach unsigned long host_clk;
754d618978dSDmitry Osipenko int err;
755a8e326a9SLucas Stach
756a8e326a9SLucas Stach if (!clock)
7573491b690SLucas Stach return sdhci_set_clock(host, clock);
758a8e326a9SLucas Stach
75957d1654eSAapo Vienamo /*
76057d1654eSAapo Vienamo * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
76157d1654eSAapo Vienamo * divider to be configured to divided the host clock by two. The SDHCI
76257d1654eSAapo Vienamo * clock divider is calculated as part of sdhci_set_clock() by
76357d1654eSAapo Vienamo * sdhci_calc_clk(). The divider is calculated from host->max_clk and
76457d1654eSAapo Vienamo * the requested clock rate.
76557d1654eSAapo Vienamo *
76657d1654eSAapo Vienamo * By setting the host->max_clk to clock * 2 the divider calculation
76757d1654eSAapo Vienamo * will always result in the correct value for DDR50/52 modes,
76857d1654eSAapo Vienamo * regardless of clock rate rounding, which may happen if the value
76957d1654eSAapo Vienamo * from clk_get_rate() is used.
77057d1654eSAapo Vienamo */
771a8e326a9SLucas Stach host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
772d618978dSDmitry Osipenko
773d618978dSDmitry Osipenko err = dev_pm_opp_set_rate(dev, host_clk);
774d618978dSDmitry Osipenko if (err)
775d618978dSDmitry Osipenko dev_err(dev, "failed to set clk rate to %luHz: %d\n",
776d618978dSDmitry Osipenko host_clk, err);
777d618978dSDmitry Osipenko
778b78870e7SPrathamesh Shete tegra_host->curr_clk_rate = clk_get_rate(pltfm_host->clk);
77957d1654eSAapo Vienamo if (tegra_host->ddr_signaling)
78057d1654eSAapo Vienamo host->max_clk = host_clk;
78157d1654eSAapo Vienamo else
782a8e326a9SLucas Stach host->max_clk = clk_get_rate(pltfm_host->clk);
783a8e326a9SLucas Stach
784e5c63d91SLucas Stach sdhci_set_clock(host, clock);
785e5c63d91SLucas Stach
786e5c63d91SLucas Stach if (tegra_host->pad_calib_required) {
787e5c63d91SLucas Stach tegra_sdhci_pad_autocalib(host);
788e5c63d91SLucas Stach tegra_host->pad_calib_required = false;
789e5c63d91SLucas Stach }
790a8e326a9SLucas Stach }
791a8e326a9SLucas Stach
tegra_sdhci_hs400_enhanced_strobe(struct mmc_host * mmc,struct mmc_ios * ios)7924fc7261dSPrathamesh Shete static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
7934fc7261dSPrathamesh Shete struct mmc_ios *ios)
7944fc7261dSPrathamesh Shete {
7954fc7261dSPrathamesh Shete struct sdhci_host *host = mmc_priv(mmc);
7964fc7261dSPrathamesh Shete u32 val;
7974fc7261dSPrathamesh Shete
7984fc7261dSPrathamesh Shete val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
7994fc7261dSPrathamesh Shete
8004fc7261dSPrathamesh Shete if (ios->enhanced_strobe) {
8014fc7261dSPrathamesh Shete val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
8024fc7261dSPrathamesh Shete /*
8034fc7261dSPrathamesh Shete * When CMD13 is sent from mmc_select_hs400es() after
8044fc7261dSPrathamesh Shete * switching to HS400ES mode, the bus is operating at
8054fc7261dSPrathamesh Shete * either MMC_HIGH_26_MAX_DTR or MMC_HIGH_52_MAX_DTR.
8064fc7261dSPrathamesh Shete * To meet Tegra SDHCI requirement at HS400ES mode, force SDHCI
8074fc7261dSPrathamesh Shete * interface clock to MMC_HS200_MAX_DTR (200 MHz) so that host
8084fc7261dSPrathamesh Shete * controller CAR clock and the interface clock are rate matched.
8094fc7261dSPrathamesh Shete */
8104fc7261dSPrathamesh Shete tegra_sdhci_set_clock(host, MMC_HS200_MAX_DTR);
8114fc7261dSPrathamesh Shete } else {
8124fc7261dSPrathamesh Shete val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
8134fc7261dSPrathamesh Shete }
8144fc7261dSPrathamesh Shete
8154fc7261dSPrathamesh Shete sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
8164fc7261dSPrathamesh Shete }
8174fc7261dSPrathamesh Shete
tegra_sdhci_get_max_clock(struct sdhci_host * host)81844350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
81944350993SAapo Vienamo {
82044350993SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
82144350993SAapo Vienamo
82244350993SAapo Vienamo return clk_round_rate(pltfm_host->clk, UINT_MAX);
82344350993SAapo Vienamo }
82444350993SAapo Vienamo
tegra_sdhci_set_dqs_trim(struct sdhci_host * host,u8 trim)825f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim)
826f5313aaaSAapo Vienamo {
827f5313aaaSAapo Vienamo u32 val;
828f5313aaaSAapo Vienamo
829f5313aaaSAapo Vienamo val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
830f5313aaaSAapo Vienamo val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK;
831f5313aaaSAapo Vienamo val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT;
832f5313aaaSAapo Vienamo sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
833f5313aaaSAapo Vienamo }
834f5313aaaSAapo Vienamo
tegra_sdhci_hs400_dll_cal(struct sdhci_host * host)835bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
836bc5568bfSAapo Vienamo {
837bc5568bfSAapo Vienamo u32 reg;
838bc5568bfSAapo Vienamo int err;
839bc5568bfSAapo Vienamo
840bc5568bfSAapo Vienamo reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
841bc5568bfSAapo Vienamo reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE;
842bc5568bfSAapo Vienamo sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
843bc5568bfSAapo Vienamo
844bc5568bfSAapo Vienamo /* 1 ms sleep, 5 ms timeout */
845bc5568bfSAapo Vienamo err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA,
846bc5568bfSAapo Vienamo reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE),
847bc5568bfSAapo Vienamo 1000, 5000);
848bc5568bfSAapo Vienamo if (err)
849bc5568bfSAapo Vienamo dev_err(mmc_dev(host->mmc),
850bc5568bfSAapo Vienamo "HS400 delay line calibration timed out\n");
851bc5568bfSAapo Vienamo }
852bc5568bfSAapo Vienamo
tegra_sdhci_tap_correction(struct sdhci_host * host,u8 thd_up,u8 thd_low,u8 fixed_tap)853ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
854ea8fc595SSowjanya Komatineni u8 thd_low, u8 fixed_tap)
855ea8fc595SSowjanya Komatineni {
856ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
857ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
858ea8fc595SSowjanya Komatineni u32 val, tun_status;
859ea8fc595SSowjanya Komatineni u8 word, bit, edge1, tap, window;
860ea8fc595SSowjanya Komatineni bool tap_result;
861ea8fc595SSowjanya Komatineni bool start_fail = false;
862ea8fc595SSowjanya Komatineni bool start_pass = false;
863ea8fc595SSowjanya Komatineni bool end_pass = false;
864ea8fc595SSowjanya Komatineni bool first_fail = false;
865ea8fc595SSowjanya Komatineni bool first_pass = false;
866ea8fc595SSowjanya Komatineni u8 start_pass_tap = 0;
867ea8fc595SSowjanya Komatineni u8 end_pass_tap = 0;
868ea8fc595SSowjanya Komatineni u8 first_fail_tap = 0;
869ea8fc595SSowjanya Komatineni u8 first_pass_tap = 0;
870ea8fc595SSowjanya Komatineni u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
871ea8fc595SSowjanya Komatineni
872ea8fc595SSowjanya Komatineni /*
873ea8fc595SSowjanya Komatineni * Read auto-tuned results and extract good valid passing window by
874ea8fc595SSowjanya Komatineni * filtering out un-wanted bubble/partial/merged windows.
875ea8fc595SSowjanya Komatineni */
876ea8fc595SSowjanya Komatineni for (word = 0; word < total_tuning_words; word++) {
877ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
878ea8fc595SSowjanya Komatineni val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
879ea8fc595SSowjanya Komatineni val |= word;
880ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
881ea8fc595SSowjanya Komatineni tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
882ea8fc595SSowjanya Komatineni bit = 0;
883ea8fc595SSowjanya Komatineni while (bit < TUNING_WORD_BIT_SIZE) {
884ea8fc595SSowjanya Komatineni tap = word * TUNING_WORD_BIT_SIZE + bit;
885ea8fc595SSowjanya Komatineni tap_result = tun_status & (1 << bit);
886ea8fc595SSowjanya Komatineni if (!tap_result && !start_fail) {
887ea8fc595SSowjanya Komatineni start_fail = true;
888ea8fc595SSowjanya Komatineni if (!first_fail) {
889ea8fc595SSowjanya Komatineni first_fail_tap = tap;
890ea8fc595SSowjanya Komatineni first_fail = true;
891ea8fc595SSowjanya Komatineni }
892ea8fc595SSowjanya Komatineni
893ea8fc595SSowjanya Komatineni } else if (tap_result && start_fail && !start_pass) {
894ea8fc595SSowjanya Komatineni start_pass_tap = tap;
895ea8fc595SSowjanya Komatineni start_pass = true;
896ea8fc595SSowjanya Komatineni if (!first_pass) {
897ea8fc595SSowjanya Komatineni first_pass_tap = tap;
898ea8fc595SSowjanya Komatineni first_pass = true;
899ea8fc595SSowjanya Komatineni }
900ea8fc595SSowjanya Komatineni
901ea8fc595SSowjanya Komatineni } else if (!tap_result && start_fail && start_pass &&
902ea8fc595SSowjanya Komatineni !end_pass) {
903ea8fc595SSowjanya Komatineni end_pass_tap = tap - 1;
904ea8fc595SSowjanya Komatineni end_pass = true;
905ea8fc595SSowjanya Komatineni } else if (tap_result && start_pass && start_fail &&
906ea8fc595SSowjanya Komatineni end_pass) {
907ea8fc595SSowjanya Komatineni window = end_pass_tap - start_pass_tap;
908ea8fc595SSowjanya Komatineni /* discard merged window and bubble window */
909ea8fc595SSowjanya Komatineni if (window >= thd_up || window < thd_low) {
910ea8fc595SSowjanya Komatineni start_pass_tap = tap;
911ea8fc595SSowjanya Komatineni end_pass = false;
912ea8fc595SSowjanya Komatineni } else {
913ea8fc595SSowjanya Komatineni /* set tap at middle of valid window */
914ea8fc595SSowjanya Komatineni tap = start_pass_tap + window / 2;
915ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = tap;
916ea8fc595SSowjanya Komatineni return;
917ea8fc595SSowjanya Komatineni }
918ea8fc595SSowjanya Komatineni }
919ea8fc595SSowjanya Komatineni
920ea8fc595SSowjanya Komatineni bit++;
921ea8fc595SSowjanya Komatineni }
922ea8fc595SSowjanya Komatineni }
923ea8fc595SSowjanya Komatineni
924ea8fc595SSowjanya Komatineni if (!first_fail) {
925d96dc68eSDan Carpenter WARN(1, "no edge detected, continue with hw tuned delay.\n");
926ea8fc595SSowjanya Komatineni } else if (first_pass) {
927ea8fc595SSowjanya Komatineni /* set tap location at fixed tap relative to the first edge */
928ea8fc595SSowjanya Komatineni edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
929ea8fc595SSowjanya Komatineni if (edge1 - 1 > fixed_tap)
930ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 - fixed_tap;
931ea8fc595SSowjanya Komatineni else
932ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = edge1 + fixed_tap;
933ea8fc595SSowjanya Komatineni }
934ea8fc595SSowjanya Komatineni }
935ea8fc595SSowjanya Komatineni
tegra_sdhci_post_tuning(struct sdhci_host * host)936ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host)
937ea8fc595SSowjanya Komatineni {
938ea8fc595SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
939ea8fc595SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
940ea8fc595SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
941ea8fc595SSowjanya Komatineni u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
942ea8fc595SSowjanya Komatineni u8 fixed_tap, start_tap, end_tap, window_width;
943ea8fc595SSowjanya Komatineni u8 thdupper, thdlower;
944ea8fc595SSowjanya Komatineni u8 num_iter;
945ea8fc595SSowjanya Komatineni u32 clk_rate_mhz, period_ps, bestcase, worstcase;
946ea8fc595SSowjanya Komatineni
947ea8fc595SSowjanya Komatineni /* retain HW tuned tap to use incase if no correction is needed */
948ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
949ea8fc595SSowjanya Komatineni tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
950ea8fc595SSowjanya Komatineni SDHCI_CLOCK_CTRL_TAP_SHIFT;
951ea8fc595SSowjanya Komatineni if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
952ea8fc595SSowjanya Komatineni min_tap_dly = soc_data->min_tap_delay;
953ea8fc595SSowjanya Komatineni max_tap_dly = soc_data->max_tap_delay;
954ea8fc595SSowjanya Komatineni clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
955ea8fc595SSowjanya Komatineni period_ps = USEC_PER_SEC / clk_rate_mhz;
956ea8fc595SSowjanya Komatineni bestcase = period_ps / min_tap_dly;
957ea8fc595SSowjanya Komatineni worstcase = period_ps / max_tap_dly;
958ea8fc595SSowjanya Komatineni /*
959ea8fc595SSowjanya Komatineni * Upper and Lower bound thresholds used to detect merged and
960ea8fc595SSowjanya Komatineni * bubble windows
961ea8fc595SSowjanya Komatineni */
962ea8fc595SSowjanya Komatineni thdupper = (2 * worstcase + bestcase) / 2;
963ea8fc595SSowjanya Komatineni thdlower = worstcase / 4;
964ea8fc595SSowjanya Komatineni /*
965ea8fc595SSowjanya Komatineni * fixed tap is used when HW tuning result contains single edge
966ea8fc595SSowjanya Komatineni * and tap is set at fixed tap delay relative to the first edge
967ea8fc595SSowjanya Komatineni */
968ea8fc595SSowjanya Komatineni avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
969ea8fc595SSowjanya Komatineni fixed_tap = avg_tap_dly / 2;
970ea8fc595SSowjanya Komatineni
971ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
972ea8fc595SSowjanya Komatineni start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
973ea8fc595SSowjanya Komatineni end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
974ea8fc595SSowjanya Komatineni SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
975ea8fc595SSowjanya Komatineni window_width = end_tap - start_tap;
976ea8fc595SSowjanya Komatineni num_iter = host->tuning_loop_count;
977ea8fc595SSowjanya Komatineni /*
978ea8fc595SSowjanya Komatineni * partial window includes edges of the tuning range.
979ea8fc595SSowjanya Komatineni * merged window includes more taps so window width is higher
980ea8fc595SSowjanya Komatineni * than upper threshold.
981ea8fc595SSowjanya Komatineni */
982ea8fc595SSowjanya Komatineni if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
983ea8fc595SSowjanya Komatineni (end_tap == num_iter - 2) || window_width >= thdupper) {
984ea8fc595SSowjanya Komatineni pr_debug("%s: Apply tuning correction\n",
985ea8fc595SSowjanya Komatineni mmc_hostname(host->mmc));
986ea8fc595SSowjanya Komatineni tegra_sdhci_tap_correction(host, thdupper, thdlower,
987ea8fc595SSowjanya Komatineni fixed_tap);
988ea8fc595SSowjanya Komatineni }
989ea8fc595SSowjanya Komatineni }
990ea8fc595SSowjanya Komatineni
991ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
992ea8fc595SSowjanya Komatineni }
993ea8fc595SSowjanya Komatineni
tegra_sdhci_execute_hw_tuning(struct mmc_host * mmc,u32 opcode)994ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
995ea8fc595SSowjanya Komatineni {
996ea8fc595SSowjanya Komatineni struct sdhci_host *host = mmc_priv(mmc);
997ea8fc595SSowjanya Komatineni int err;
998ea8fc595SSowjanya Komatineni
999ea8fc595SSowjanya Komatineni err = sdhci_execute_tuning(mmc, opcode);
1000ea8fc595SSowjanya Komatineni if (!err && !host->tuning_err)
1001ea8fc595SSowjanya Komatineni tegra_sdhci_post_tuning(host);
1002ea8fc595SSowjanya Komatineni
1003ea8fc595SSowjanya Komatineni return err;
1004ea8fc595SSowjanya Komatineni }
1005ea8fc595SSowjanya Komatineni
tegra_sdhci_set_uhs_signaling(struct sdhci_host * host,unsigned timing)1006c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
1007c2c09678SAapo Vienamo unsigned timing)
1008c3c2384cSLucas Stach {
1009d4501d8eSAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1010d4501d8eSAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1011c2c09678SAapo Vienamo bool set_default_tap = false;
1012f5313aaaSAapo Vienamo bool set_dqs_trim = false;
1013bc5568bfSAapo Vienamo bool do_hs400_dll_cal = false;
1014ea8fc595SSowjanya Komatineni u8 iter = TRIES_256;
1015ea8fc595SSowjanya Komatineni u32 val;
1016c3c2384cSLucas Stach
101792cd1667SSowjanya Komatineni tegra_host->ddr_signaling = false;
1018c2c09678SAapo Vienamo switch (timing) {
1019c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR50:
1020ea8fc595SSowjanya Komatineni break;
1021c2c09678SAapo Vienamo case MMC_TIMING_UHS_SDR104:
1022c2c09678SAapo Vienamo case MMC_TIMING_MMC_HS200:
1023c2c09678SAapo Vienamo /* Don't set default tap on tunable modes. */
1024ea8fc595SSowjanya Komatineni iter = TRIES_128;
1025c2c09678SAapo Vienamo break;
1026f5313aaaSAapo Vienamo case MMC_TIMING_MMC_HS400:
1027f5313aaaSAapo Vienamo set_dqs_trim = true;
1028bc5568bfSAapo Vienamo do_hs400_dll_cal = true;
1029ea8fc595SSowjanya Komatineni iter = TRIES_128;
1030f5313aaaSAapo Vienamo break;
1031c2c09678SAapo Vienamo case MMC_TIMING_MMC_DDR52:
1032c2c09678SAapo Vienamo case MMC_TIMING_UHS_DDR50:
1033c2c09678SAapo Vienamo tegra_host->ddr_signaling = true;
1034c2c09678SAapo Vienamo set_default_tap = true;
1035c2c09678SAapo Vienamo break;
1036c2c09678SAapo Vienamo default:
1037c2c09678SAapo Vienamo set_default_tap = true;
1038c2c09678SAapo Vienamo break;
1039d4501d8eSAapo Vienamo }
1040c2c09678SAapo Vienamo
1041ea8fc595SSowjanya Komatineni val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
1042ea8fc595SSowjanya Komatineni val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
1043ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
1044ea8fc595SSowjanya Komatineni SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
1045ea8fc595SSowjanya Komatineni val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
1046ea8fc595SSowjanya Komatineni 0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
1047ea8fc595SSowjanya Komatineni 1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
1048ea8fc595SSowjanya Komatineni sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
1049ea8fc595SSowjanya Komatineni sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
1050ea8fc595SSowjanya Komatineni
1051ea8fc595SSowjanya Komatineni host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
1052ea8fc595SSowjanya Komatineni
1053c2c09678SAapo Vienamo sdhci_set_uhs_signaling(host, timing);
1054c2c09678SAapo Vienamo
1055c2c09678SAapo Vienamo tegra_sdhci_pad_autocalib(host);
1056c2c09678SAapo Vienamo
1057ea8fc595SSowjanya Komatineni if (tegra_host->tuned_tap_delay && !set_default_tap)
1058ea8fc595SSowjanya Komatineni tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
1059ea8fc595SSowjanya Komatineni else
1060c2c09678SAapo Vienamo tegra_sdhci_set_tap(host, tegra_host->default_tap);
1061f5313aaaSAapo Vienamo
1062f5313aaaSAapo Vienamo if (set_dqs_trim)
1063f5313aaaSAapo Vienamo tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim);
1064bc5568bfSAapo Vienamo
1065bc5568bfSAapo Vienamo if (do_hs400_dll_cal)
1066bc5568bfSAapo Vienamo tegra_sdhci_hs400_dll_cal(host);
1067c3c2384cSLucas Stach }
1068c3c2384cSLucas Stach
tegra_sdhci_execute_tuning(struct sdhci_host * host,u32 opcode)1069c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
1070c3c2384cSLucas Stach {
1071c3c2384cSLucas Stach unsigned int min, max;
1072c3c2384cSLucas Stach
1073c3c2384cSLucas Stach /*
1074c3c2384cSLucas Stach * Start search for minimum tap value at 10, as smaller values are
1075c3c2384cSLucas Stach * may wrongly be reported as working but fail at higher speeds,
1076c3c2384cSLucas Stach * according to the TRM.
1077c3c2384cSLucas Stach */
1078c3c2384cSLucas Stach min = 10;
1079c3c2384cSLucas Stach while (min < 255) {
1080c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min);
1081c3c2384cSLucas Stach if (!mmc_send_tuning(host->mmc, opcode, NULL))
1082c3c2384cSLucas Stach break;
1083c3c2384cSLucas Stach min++;
1084c3c2384cSLucas Stach }
1085c3c2384cSLucas Stach
1086c3c2384cSLucas Stach /* Find the maximum tap value that still passes. */
1087c3c2384cSLucas Stach max = min + 1;
1088c3c2384cSLucas Stach while (max < 255) {
1089c3c2384cSLucas Stach tegra_sdhci_set_tap(host, max);
1090c3c2384cSLucas Stach if (mmc_send_tuning(host->mmc, opcode, NULL)) {
1091c3c2384cSLucas Stach max--;
1092c3c2384cSLucas Stach break;
1093c3c2384cSLucas Stach }
1094c3c2384cSLucas Stach max++;
1095c3c2384cSLucas Stach }
1096c3c2384cSLucas Stach
1097c3c2384cSLucas Stach /* The TRM states the ideal tap value is at 75% in the passing range. */
1098c3c2384cSLucas Stach tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
1099c3c2384cSLucas Stach
1100c3c2384cSLucas Stach return mmc_send_tuning(host->mmc, opcode, NULL);
1101c3c2384cSLucas Stach }
1102c3c2384cSLucas Stach
sdhci_tegra_start_signal_voltage_switch(struct mmc_host * mmc,struct mmc_ios * ios)110386ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
110486ac2f8bSAapo Vienamo struct mmc_ios *ios)
110586ac2f8bSAapo Vienamo {
110686ac2f8bSAapo Vienamo struct sdhci_host *host = mmc_priv(mmc);
110744babea2SAapo Vienamo struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
110844babea2SAapo Vienamo struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
110986ac2f8bSAapo Vienamo int ret = 0;
111086ac2f8bSAapo Vienamo
111186ac2f8bSAapo Vienamo if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
1112de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
111386ac2f8bSAapo Vienamo if (ret < 0)
111486ac2f8bSAapo Vienamo return ret;
111586ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios);
111686ac2f8bSAapo Vienamo } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
111786ac2f8bSAapo Vienamo ret = sdhci_start_signal_voltage_switch(mmc, ios);
111886ac2f8bSAapo Vienamo if (ret < 0)
111986ac2f8bSAapo Vienamo return ret;
1120de25fa5aSSowjanya Komatineni ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
112186ac2f8bSAapo Vienamo }
112286ac2f8bSAapo Vienamo
112344babea2SAapo Vienamo if (tegra_host->pad_calib_required)
112444babea2SAapo Vienamo tegra_sdhci_pad_autocalib(host);
112544babea2SAapo Vienamo
112686ac2f8bSAapo Vienamo return ret;
112786ac2f8bSAapo Vienamo }
112886ac2f8bSAapo Vienamo
tegra_sdhci_init_pinctrl_info(struct device * dev,struct sdhci_tegra * tegra_host)112986ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
113086ac2f8bSAapo Vienamo struct sdhci_tegra *tegra_host)
113186ac2f8bSAapo Vienamo {
113286ac2f8bSAapo Vienamo tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
113386ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
113486ac2f8bSAapo Vienamo dev_dbg(dev, "No pinctrl info, err: %ld\n",
113586ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_sdmmc));
113686ac2f8bSAapo Vienamo return -1;
113786ac2f8bSAapo Vienamo }
113886ac2f8bSAapo Vienamo
1139de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
1140de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
1141de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
1142de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
1143de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_1v8_drv = NULL;
1144de25fa5aSSowjanya Komatineni }
1145de25fa5aSSowjanya Komatineni
1146de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
1147de25fa5aSSowjanya Komatineni tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
1148de25fa5aSSowjanya Komatineni if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
1149de25fa5aSSowjanya Komatineni if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
1150de25fa5aSSowjanya Komatineni tegra_host->pinctrl_state_3v3_drv = NULL;
1151de25fa5aSSowjanya Komatineni }
1152de25fa5aSSowjanya Komatineni
115386ac2f8bSAapo Vienamo tegra_host->pinctrl_state_3v3 =
115486ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
115586ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
115686ac2f8bSAapo Vienamo dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
115786ac2f8bSAapo Vienamo PTR_ERR(tegra_host->pinctrl_state_3v3));
115886ac2f8bSAapo Vienamo return -1;
115986ac2f8bSAapo Vienamo }
116086ac2f8bSAapo Vienamo
116186ac2f8bSAapo Vienamo tegra_host->pinctrl_state_1v8 =
116286ac2f8bSAapo Vienamo pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
116386ac2f8bSAapo Vienamo if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
116486ac2f8bSAapo Vienamo dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
1165e5378247SYueHaibing PTR_ERR(tegra_host->pinctrl_state_1v8));
116686ac2f8bSAapo Vienamo return -1;
116786ac2f8bSAapo Vienamo }
116886ac2f8bSAapo Vienamo
116986ac2f8bSAapo Vienamo tegra_host->pad_control_available = true;
117086ac2f8bSAapo Vienamo
117186ac2f8bSAapo Vienamo return 0;
117286ac2f8bSAapo Vienamo }
117386ac2f8bSAapo Vienamo
tegra_sdhci_voltage_switch(struct sdhci_host * host)1174e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
1175e5c63d91SLucas Stach {
1176e5c63d91SLucas Stach struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1177e5c63d91SLucas Stach struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1178e5c63d91SLucas Stach const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1179e5c63d91SLucas Stach
1180e5c63d91SLucas Stach if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
1181e5c63d91SLucas Stach tegra_host->pad_calib_required = true;
1182e5c63d91SLucas Stach }
1183e5c63d91SLucas Stach
tegra_cqhci_writel(struct cqhci_host * cq_host,u32 val,int reg)1184b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg)
1185b7754428SSowjanya Komatineni {
1186b7754428SSowjanya Komatineni struct mmc_host *mmc = cq_host->mmc;
11875ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc);
1188b7754428SSowjanya Komatineni u8 ctrl;
1189b7754428SSowjanya Komatineni ktime_t timeout;
1190b7754428SSowjanya Komatineni bool timed_out;
1191b7754428SSowjanya Komatineni
1192b7754428SSowjanya Komatineni /*
1193b7754428SSowjanya Komatineni * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to
1194b7754428SSowjanya Komatineni * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need
1195b7754428SSowjanya Komatineni * to be re-configured.
1196b7754428SSowjanya Komatineni * Tegra CQHCI/SDHCI prevents write access to block size register when
1197b7754428SSowjanya Komatineni * CQE is unhalted. So handling CQE resume sequence here to configure
1198b7754428SSowjanya Komatineni * SDHCI block registers prior to exiting CQE halt state.
1199b7754428SSowjanya Komatineni */
1200b7754428SSowjanya Komatineni if (reg == CQHCI_CTL && !(val & CQHCI_HALT) &&
1201b7754428SSowjanya Komatineni cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) {
12025ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
1203b7754428SSowjanya Komatineni sdhci_cqe_enable(mmc);
1204b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg);
1205b7754428SSowjanya Komatineni timeout = ktime_add_us(ktime_get(), 50);
1206b7754428SSowjanya Komatineni while (1) {
1207b7754428SSowjanya Komatineni timed_out = ktime_compare(ktime_get(), timeout) > 0;
1208b7754428SSowjanya Komatineni ctrl = cqhci_readl(cq_host, CQHCI_CTL);
1209b7754428SSowjanya Komatineni if (!(ctrl & CQHCI_HALT) || timed_out)
1210b7754428SSowjanya Komatineni break;
1211b7754428SSowjanya Komatineni }
1212b7754428SSowjanya Komatineni /*
1213b7754428SSowjanya Komatineni * CQE usually resumes very quick, but incase if Tegra CQE
1214b7754428SSowjanya Komatineni * doesn't resume retry unhalt.
1215b7754428SSowjanya Komatineni */
1216b7754428SSowjanya Komatineni if (timed_out)
1217b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg);
1218b7754428SSowjanya Komatineni } else {
1219b7754428SSowjanya Komatineni writel(val, cq_host->mmio + reg);
1220b7754428SSowjanya Komatineni }
1221b7754428SSowjanya Komatineni }
1222b7754428SSowjanya Komatineni
sdhci_tegra_update_dcmd_desc(struct mmc_host * mmc,struct mmc_request * mrq,u64 * data)1223c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc,
1224c6e7ab90SSowjanya Komatineni struct mmc_request *mrq, u64 *data)
1225c6e7ab90SSowjanya Komatineni {
1226c6e7ab90SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc));
1227c6e7ab90SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1228c6e7ab90SSowjanya Komatineni const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1229c6e7ab90SSowjanya Komatineni
1230c6e7ab90SSowjanya Komatineni if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING &&
1231c6e7ab90SSowjanya Komatineni mrq->cmd->flags & MMC_RSP_R1B)
1232c6e7ab90SSowjanya Komatineni *data |= CQHCI_CMD_TIMING(1);
1233c6e7ab90SSowjanya Komatineni }
1234c6e7ab90SSowjanya Komatineni
sdhci_tegra_cqe_enable(struct mmc_host * mmc)12353c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
12363c4019f9SSowjanya Komatineni {
12373c4019f9SSowjanya Komatineni struct cqhci_host *cq_host = mmc->cqe_private;
12385ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc);
1239b7754428SSowjanya Komatineni u32 val;
12403c4019f9SSowjanya Komatineni
12413c4019f9SSowjanya Komatineni /*
1242b7754428SSowjanya Komatineni * Tegra CQHCI/SDMMC design prevents write access to sdhci block size
1243b7754428SSowjanya Komatineni * register when CQE is enabled and unhalted.
1244b7754428SSowjanya Komatineni * CQHCI driver enables CQE prior to activation, so disable CQE before
1245b7754428SSowjanya Komatineni * programming block size in sdhci controller and enable it back.
12463c4019f9SSowjanya Komatineni */
1247b7754428SSowjanya Komatineni if (!cq_host->activated) {
1248b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_CFG);
1249b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE)
1250b7754428SSowjanya Komatineni cqhci_writel(cq_host, (val & ~CQHCI_ENABLE),
1251b7754428SSowjanya Komatineni CQHCI_CFG);
12525ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, SDHCI_TEGRA_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
12533c4019f9SSowjanya Komatineni sdhci_cqe_enable(mmc);
1254b7754428SSowjanya Komatineni if (val & CQHCI_ENABLE)
1255b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_CFG);
1256b7754428SSowjanya Komatineni }
12573c4019f9SSowjanya Komatineni
1258b7754428SSowjanya Komatineni /*
1259b7754428SSowjanya Komatineni * CMD CRC errors are seen sometimes with some eMMC devices when status
1260b7754428SSowjanya Komatineni * command is sent during transfer of last data block which is the
1261b7754428SSowjanya Komatineni * default case as send status command block counter (CBC) is 1.
1262b7754428SSowjanya Komatineni * Recommended fix to set CBC to 0 allowing send status command only
1263b7754428SSowjanya Komatineni * when data lines are idle.
1264b7754428SSowjanya Komatineni */
1265b7754428SSowjanya Komatineni val = cqhci_readl(cq_host, CQHCI_SSC1);
1266b7754428SSowjanya Komatineni val &= ~CQHCI_SSC1_CBC_MASK;
1267b7754428SSowjanya Komatineni cqhci_writel(cq_host, val, CQHCI_SSC1);
12683c4019f9SSowjanya Komatineni }
12693c4019f9SSowjanya Komatineni
sdhci_tegra_dumpregs(struct mmc_host * mmc)12703c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
12713c4019f9SSowjanya Komatineni {
12723c4019f9SSowjanya Komatineni sdhci_dumpregs(mmc_priv(mmc));
12733c4019f9SSowjanya Komatineni }
12743c4019f9SSowjanya Komatineni
sdhci_tegra_cqhci_irq(struct sdhci_host * host,u32 intmask)12753c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
12763c4019f9SSowjanya Komatineni {
12773c4019f9SSowjanya Komatineni int cmd_error = 0;
12783c4019f9SSowjanya Komatineni int data_error = 0;
12793c4019f9SSowjanya Komatineni
12803c4019f9SSowjanya Komatineni if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
12813c4019f9SSowjanya Komatineni return intmask;
12823c4019f9SSowjanya Komatineni
12833c4019f9SSowjanya Komatineni cqhci_irq(host->mmc, intmask, cmd_error, data_error);
12843c4019f9SSowjanya Komatineni
12853c4019f9SSowjanya Komatineni return 0;
12863c4019f9SSowjanya Komatineni }
12873c4019f9SSowjanya Komatineni
tegra_sdhci_set_timeout(struct sdhci_host * host,struct mmc_command * cmd)12885e958e4aSSowjanya Komatineni static void tegra_sdhci_set_timeout(struct sdhci_host *host,
12895e958e4aSSowjanya Komatineni struct mmc_command *cmd)
12905e958e4aSSowjanya Komatineni {
12915e958e4aSSowjanya Komatineni u32 val;
12925e958e4aSSowjanya Komatineni
12935e958e4aSSowjanya Komatineni /*
12945e958e4aSSowjanya Komatineni * HW busy detection timeout is based on programmed data timeout
12955e958e4aSSowjanya Komatineni * counter and maximum supported timeout is 11s which may not be
12965e958e4aSSowjanya Komatineni * enough for long operations like cache flush, sleep awake, erase.
12975e958e4aSSowjanya Komatineni *
12985e958e4aSSowjanya Komatineni * ERASE_TIMEOUT_LIMIT bit of VENDOR_MISC_CTRL register allows
12995e958e4aSSowjanya Komatineni * host controller to wait for busy state until the card is busy
13005e958e4aSSowjanya Komatineni * without HW timeout.
13015e958e4aSSowjanya Komatineni *
13025e958e4aSSowjanya Komatineni * So, use infinite busy wait mode for operations that may take
13035e958e4aSSowjanya Komatineni * more than maximum HW busy timeout of 11s otherwise use finite
13045e958e4aSSowjanya Komatineni * busy wait mode.
13055e958e4aSSowjanya Komatineni */
13065e958e4aSSowjanya Komatineni val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
1307fcc541feSWolfram Sang if (cmd && cmd->busy_timeout >= 11 * MSEC_PER_SEC)
13085e958e4aSSowjanya Komatineni val |= SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT;
13095e958e4aSSowjanya Komatineni else
13105e958e4aSSowjanya Komatineni val &= ~SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT;
13115e958e4aSSowjanya Komatineni sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_MISC_CTRL);
13125e958e4aSSowjanya Komatineni
13135e958e4aSSowjanya Komatineni __sdhci_set_timeout(host, cmd);
13145e958e4aSSowjanya Komatineni }
13155e958e4aSSowjanya Komatineni
sdhci_tegra_cqe_pre_enable(struct mmc_host * mmc)13165ec6fa5aSAniruddha Tvs Rao static void sdhci_tegra_cqe_pre_enable(struct mmc_host *mmc)
13175ec6fa5aSAniruddha Tvs Rao {
13185ec6fa5aSAniruddha Tvs Rao struct cqhci_host *cq_host = mmc->cqe_private;
13195ec6fa5aSAniruddha Tvs Rao u32 reg;
13205ec6fa5aSAniruddha Tvs Rao
13215ec6fa5aSAniruddha Tvs Rao reg = cqhci_readl(cq_host, CQHCI_CFG);
13225ec6fa5aSAniruddha Tvs Rao reg |= CQHCI_ENABLE;
13235ec6fa5aSAniruddha Tvs Rao cqhci_writel(cq_host, reg, CQHCI_CFG);
13245ec6fa5aSAniruddha Tvs Rao }
13255ec6fa5aSAniruddha Tvs Rao
sdhci_tegra_cqe_post_disable(struct mmc_host * mmc)13265ec6fa5aSAniruddha Tvs Rao static void sdhci_tegra_cqe_post_disable(struct mmc_host *mmc)
13275ec6fa5aSAniruddha Tvs Rao {
13285ec6fa5aSAniruddha Tvs Rao struct cqhci_host *cq_host = mmc->cqe_private;
13295ec6fa5aSAniruddha Tvs Rao struct sdhci_host *host = mmc_priv(mmc);
13305ec6fa5aSAniruddha Tvs Rao u32 reg;
13315ec6fa5aSAniruddha Tvs Rao
13325ec6fa5aSAniruddha Tvs Rao reg = cqhci_readl(cq_host, CQHCI_CFG);
13335ec6fa5aSAniruddha Tvs Rao reg &= ~CQHCI_ENABLE;
13345ec6fa5aSAniruddha Tvs Rao cqhci_writel(cq_host, reg, CQHCI_CFG);
13355ec6fa5aSAniruddha Tvs Rao sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
13365ec6fa5aSAniruddha Tvs Rao }
13375ec6fa5aSAniruddha Tvs Rao
13383c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
1339b7754428SSowjanya Komatineni .write_l = tegra_cqhci_writel,
13403c4019f9SSowjanya Komatineni .enable = sdhci_tegra_cqe_enable,
13413c4019f9SSowjanya Komatineni .disable = sdhci_cqe_disable,
13423c4019f9SSowjanya Komatineni .dumpregs = sdhci_tegra_dumpregs,
1343c6e7ab90SSowjanya Komatineni .update_dcmd_desc = sdhci_tegra_update_dcmd_desc,
13445ec6fa5aSAniruddha Tvs Rao .pre_enable = sdhci_tegra_cqe_pre_enable,
13455ec6fa5aSAniruddha Tvs Rao .post_disable = sdhci_tegra_cqe_post_disable,
13463c4019f9SSowjanya Komatineni };
13473c4019f9SSowjanya Komatineni
tegra_sdhci_set_dma_mask(struct sdhci_host * host)1348b960bc44SNicolin Chen static int tegra_sdhci_set_dma_mask(struct sdhci_host *host)
1349b960bc44SNicolin Chen {
1350b960bc44SNicolin Chen struct sdhci_pltfm_host *platform = sdhci_priv(host);
1351b960bc44SNicolin Chen struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform);
1352b960bc44SNicolin Chen const struct sdhci_tegra_soc_data *soc = tegra->soc_data;
1353b960bc44SNicolin Chen struct device *dev = mmc_dev(host->mmc);
1354b960bc44SNicolin Chen
1355b960bc44SNicolin Chen if (soc->dma_mask)
1356b960bc44SNicolin Chen return dma_set_mask_and_coherent(dev, soc->dma_mask);
1357b960bc44SNicolin Chen
1358b960bc44SNicolin Chen return 0;
1359b960bc44SNicolin Chen }
1360b960bc44SNicolin Chen
1361c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
13620f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro,
136385d6509dSShawn Guo .read_w = tegra_sdhci_readw,
136485d6509dSShawn Guo .write_l = tegra_sdhci_writel,
1365a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock,
1366b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask,
136714b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width,
136803231f9bSRussell King .reset = tegra_sdhci_reset,
1369c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning,
1370a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1371e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch,
137244350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock,
137385d6509dSShawn Guo };
137403d2bfc8SOlof Johansson
13751db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
137685d6509dSShawn Guo .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
137785d6509dSShawn Guo SDHCI_QUIRK_SINGLE_POWER_WRITE |
137885d6509dSShawn Guo SDHCI_QUIRK_NO_HISPD_BIT |
1379f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1380f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
138185d6509dSShawn Guo .ops = &tegra_sdhci_ops,
138285d6509dSShawn Guo };
138385d6509dSShawn Guo
1384d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
13853e44a1a7SStephen Warren .pdata = &sdhci_tegra20_pdata,
1386b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32),
13873e44a1a7SStephen Warren .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
13881743fa54SDmitry Osipenko NVQUIRK_HAS_ANDROID_GPT_SECTOR |
13893e44a1a7SStephen Warren NVQUIRK_ENABLE_BLOCK_GAP_DET,
13903e44a1a7SStephen Warren };
13913e44a1a7SStephen Warren
13921db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
13933e44a1a7SStephen Warren .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13943e44a1a7SStephen Warren SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13953e44a1a7SStephen Warren SDHCI_QUIRK_SINGLE_POWER_WRITE |
13963e44a1a7SStephen Warren SDHCI_QUIRK_NO_HISPD_BIT |
1397f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1398f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1399127407e3SStefan Agner .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1400726df1d5SStefan Agner SDHCI_QUIRK2_BROKEN_HS200 |
1401726df1d5SStefan Agner /*
1402726df1d5SStefan Agner * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
1403726df1d5SStefan Agner * though no command operation was in progress."
1404726df1d5SStefan Agner *
1405726df1d5SStefan Agner * The exact reason is unknown, as the same hardware seems
1406726df1d5SStefan Agner * to support Auto CMD23 on a downstream 3.1 kernel.
1407726df1d5SStefan Agner */
1408726df1d5SStefan Agner SDHCI_QUIRK2_ACMD23_BROKEN,
14093e44a1a7SStephen Warren .ops = &tegra_sdhci_ops,
14103e44a1a7SStephen Warren };
14113e44a1a7SStephen Warren
1412d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
14133e44a1a7SStephen Warren .pdata = &sdhci_tegra30_pdata,
1414b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32),
14153145351aSAndrew Bresticker .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
14167ad2ed1dSLucas Stach NVQUIRK_ENABLE_SDR50 |
1417e5c63d91SLucas Stach NVQUIRK_ENABLE_SDR104 |
14181743fa54SDmitry Osipenko NVQUIRK_HAS_ANDROID_GPT_SECTOR |
1419e5c63d91SLucas Stach NVQUIRK_HAS_PADCALIB,
14203e44a1a7SStephen Warren };
14213e44a1a7SStephen Warren
142201df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
14230f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro,
142401df7ecdSRhyland Klein .read_w = tegra_sdhci_readw,
142501df7ecdSRhyland Klein .write_w = tegra_sdhci_writew,
142601df7ecdSRhyland Klein .write_l = tegra_sdhci_writel,
1427a8e326a9SLucas Stach .set_clock = tegra_sdhci_set_clock,
1428b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask,
142914b04c6aSMichał Mirosław .set_bus_width = sdhci_set_bus_width,
143001df7ecdSRhyland Klein .reset = tegra_sdhci_reset,
1431c3c2384cSLucas Stach .platform_execute_tuning = tegra_sdhci_execute_tuning,
1432a8e326a9SLucas Stach .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1433e5c63d91SLucas Stach .voltage_switch = tegra_sdhci_voltage_switch,
143444350993SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock,
143501df7ecdSRhyland Klein };
143601df7ecdSRhyland Klein
14371db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
14385ebf2552SRhyland Klein .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
14395ebf2552SRhyland Klein SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
14405ebf2552SRhyland Klein SDHCI_QUIRK_SINGLE_POWER_WRITE |
14415ebf2552SRhyland Klein SDHCI_QUIRK_NO_HISPD_BIT |
1442f9260355SAndrew Bresticker SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1443f9260355SAndrew Bresticker SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1444a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
144501df7ecdSRhyland Klein .ops = &tegra114_sdhci_ops,
14465ebf2552SRhyland Klein };
14475ebf2552SRhyland Klein
1448d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
14495ebf2552SRhyland Klein .pdata = &sdhci_tegra114_pdata,
1450b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(32),
14511743fa54SDmitry Osipenko .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR,
14527bf037d6SJon Hunter };
14537bf037d6SJon Hunter
14544ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
14554ae12588SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
14564ae12588SThierry Reding SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
14574ae12588SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE |
14584ae12588SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT |
14594ae12588SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
14604ae12588SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1461b960bc44SNicolin Chen .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
14624ae12588SThierry Reding .ops = &tegra114_sdhci_ops,
14634ae12588SThierry Reding };
14644ae12588SThierry Reding
14654ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
14664ae12588SThierry Reding .pdata = &sdhci_tegra124_pdata,
1467b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34),
14681743fa54SDmitry Osipenko .nvquirks = NVQUIRK_HAS_ANDROID_GPT_SECTOR,
14694ae12588SThierry Reding };
14704ae12588SThierry Reding
14711070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = {
14720f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro,
14731070e83aSAapo Vienamo .read_w = tegra_sdhci_readw,
147438a284d9SAapo Vienamo .write_w = tegra210_sdhci_writew,
14751070e83aSAapo Vienamo .write_l = tegra_sdhci_writel,
14761070e83aSAapo Vienamo .set_clock = tegra_sdhci_set_clock,
1477b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask,
14781070e83aSAapo Vienamo .set_bus_width = sdhci_set_bus_width,
14791070e83aSAapo Vienamo .reset = tegra_sdhci_reset,
14801070e83aSAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
14811070e83aSAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch,
14821070e83aSAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock,
14835e958e4aSSowjanya Komatineni .set_timeout = tegra_sdhci_set_timeout,
14841070e83aSAapo Vienamo };
14851070e83aSAapo Vienamo
1486b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
1487b5a84ecfSThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
1488b5a84ecfSThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE |
1489b5a84ecfSThierry Reding SDHCI_QUIRK_NO_HISPD_BIT |
1490a8e326a9SLucas Stach SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1491a8e326a9SLucas Stach SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1492a8e326a9SLucas Stach .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
14931070e83aSAapo Vienamo .ops = &tegra210_sdhci_ops,
1494b5a84ecfSThierry Reding };
1495b5a84ecfSThierry Reding
1496b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
1497b5a84ecfSThierry Reding .pdata = &sdhci_tegra210_pdata,
1498b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(34),
1499d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1500d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB |
15013559d4a6SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
15023559d4a6SAapo Vienamo NVQUIRK_ENABLE_SDR50 |
15038048822bSSowjanya Komatineni NVQUIRK_ENABLE_SDR104 |
15048048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK,
1505ea8fc595SSowjanya Komatineni .min_tap_delay = 106,
1506ea8fc595SSowjanya Komatineni .max_tap_delay = 185,
1507b5a84ecfSThierry Reding };
1508b5a84ecfSThierry Reding
150938a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = {
15100f686ca9SDmitry Osipenko .get_ro = tegra_sdhci_get_ro,
151138a284d9SAapo Vienamo .read_w = tegra_sdhci_readw,
151238a284d9SAapo Vienamo .write_l = tegra_sdhci_writel,
151338a284d9SAapo Vienamo .set_clock = tegra_sdhci_set_clock,
1514b960bc44SNicolin Chen .set_dma_mask = tegra_sdhci_set_dma_mask,
151538a284d9SAapo Vienamo .set_bus_width = sdhci_set_bus_width,
151638a284d9SAapo Vienamo .reset = tegra_sdhci_reset,
151738a284d9SAapo Vienamo .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
151838a284d9SAapo Vienamo .voltage_switch = tegra_sdhci_voltage_switch,
151938a284d9SAapo Vienamo .get_max_clock = tegra_sdhci_get_max_clock,
15203c4019f9SSowjanya Komatineni .irq = sdhci_tegra_cqhci_irq,
15215e958e4aSSowjanya Komatineni .set_timeout = tegra_sdhci_set_timeout,
152238a284d9SAapo Vienamo };
152338a284d9SAapo Vienamo
15244346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
15254346b7c7SThierry Reding .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
15264346b7c7SThierry Reding SDHCI_QUIRK_SINGLE_POWER_WRITE |
15274346b7c7SThierry Reding SDHCI_QUIRK_NO_HISPD_BIT |
15284346b7c7SThierry Reding SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
15294346b7c7SThierry Reding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1530acc13958SPrathamesh Shete .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1531acc13958SPrathamesh Shete SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER,
153238a284d9SAapo Vienamo .ops = &tegra186_sdhci_ops,
15334346b7c7SThierry Reding };
15344346b7c7SThierry Reding
15354346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
15364346b7c7SThierry Reding .pdata = &sdhci_tegra186_pdata,
1537b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(40),
1538d943f6e9SAapo Vienamo .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1539d4501d8eSAapo Vienamo NVQUIRK_HAS_PADCALIB |
15402ad50051SAapo Vienamo NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
15412ad50051SAapo Vienamo NVQUIRK_ENABLE_SDR50 |
1542c6e7ab90SSowjanya Komatineni NVQUIRK_ENABLE_SDR104 |
15438048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK |
1544c6e7ab90SSowjanya Komatineni NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING,
1545ea8fc595SSowjanya Komatineni .min_tap_delay = 84,
1546ea8fc595SSowjanya Komatineni .max_tap_delay = 136,
1547ea8fc595SSowjanya Komatineni };
1548ea8fc595SSowjanya Komatineni
1549ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
1550ea8fc595SSowjanya Komatineni .pdata = &sdhci_tegra186_pdata,
1551b960bc44SNicolin Chen .dma_mask = DMA_BIT_MASK(39),
1552ea8fc595SSowjanya Komatineni .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1553ea8fc595SSowjanya Komatineni NVQUIRK_HAS_PADCALIB |
1554ea8fc595SSowjanya Komatineni NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
1555ea8fc595SSowjanya Komatineni NVQUIRK_ENABLE_SDR50 |
15568048822bSSowjanya Komatineni NVQUIRK_ENABLE_SDR104 |
15578048822bSSowjanya Komatineni NVQUIRK_HAS_TMCLK,
1558ea8fc595SSowjanya Komatineni .min_tap_delay = 96,
1559ea8fc595SSowjanya Komatineni .max_tap_delay = 139,
15604346b7c7SThierry Reding };
15614346b7c7SThierry Reding
15628f00ad01SPrathamesh Shete static const struct sdhci_tegra_soc_data soc_data_tegra234 = {
15638f00ad01SPrathamesh Shete .pdata = &sdhci_tegra186_pdata,
15648f00ad01SPrathamesh Shete .dma_mask = DMA_BIT_MASK(39),
15658f00ad01SPrathamesh Shete .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
15668f00ad01SPrathamesh Shete NVQUIRK_HAS_PADCALIB |
15678f00ad01SPrathamesh Shete NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
15688f00ad01SPrathamesh Shete NVQUIRK_ENABLE_SDR50 |
15698f00ad01SPrathamesh Shete NVQUIRK_ENABLE_SDR104 |
157003813c81SPrathamesh Shete NVQUIRK_PROGRAM_STREAMID |
15718f00ad01SPrathamesh Shete NVQUIRK_HAS_TMCLK,
15728f00ad01SPrathamesh Shete .min_tap_delay = 95,
15738f00ad01SPrathamesh Shete .max_tap_delay = 111,
15748f00ad01SPrathamesh Shete };
15758f00ad01SPrathamesh Shete
1576498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
15778f00ad01SPrathamesh Shete { .compatible = "nvidia,tegra234-sdhci", .data = &soc_data_tegra234 },
1578ea8fc595SSowjanya Komatineni { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
15794346b7c7SThierry Reding { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
1580b5a84ecfSThierry Reding { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
15814ae12588SThierry Reding { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
15825ebf2552SRhyland Klein { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
15833e44a1a7SStephen Warren { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
15843e44a1a7SStephen Warren { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
1585275173b2SGrant Likely {}
1586275173b2SGrant Likely };
1587e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
1588275173b2SGrant Likely
sdhci_tegra_add_host(struct sdhci_host * host)15893c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host)
15903c4019f9SSowjanya Komatineni {
15913c4019f9SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
15923c4019f9SSowjanya Komatineni struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
15933c4019f9SSowjanya Komatineni struct cqhci_host *cq_host;
15943c4019f9SSowjanya Komatineni bool dma64;
15953c4019f9SSowjanya Komatineni int ret;
15963c4019f9SSowjanya Komatineni
15973c4019f9SSowjanya Komatineni if (!tegra_host->enable_hwcq)
15983c4019f9SSowjanya Komatineni return sdhci_add_host(host);
15993c4019f9SSowjanya Komatineni
16003c4019f9SSowjanya Komatineni sdhci_enable_v4_mode(host);
16013c4019f9SSowjanya Komatineni
16023c4019f9SSowjanya Komatineni ret = sdhci_setup_host(host);
16033c4019f9SSowjanya Komatineni if (ret)
16043c4019f9SSowjanya Komatineni return ret;
16053c4019f9SSowjanya Komatineni
16063c4019f9SSowjanya Komatineni host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
16073c4019f9SSowjanya Komatineni
1608bac53336SJisheng Zhang cq_host = devm_kzalloc(mmc_dev(host->mmc),
16093c4019f9SSowjanya Komatineni sizeof(*cq_host), GFP_KERNEL);
16103c4019f9SSowjanya Komatineni if (!cq_host) {
16113c4019f9SSowjanya Komatineni ret = -ENOMEM;
16123c4019f9SSowjanya Komatineni goto cleanup;
16133c4019f9SSowjanya Komatineni }
16143c4019f9SSowjanya Komatineni
16153c4019f9SSowjanya Komatineni cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
16163c4019f9SSowjanya Komatineni cq_host->ops = &sdhci_tegra_cqhci_ops;
16173c4019f9SSowjanya Komatineni
16183c4019f9SSowjanya Komatineni dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
16193c4019f9SSowjanya Komatineni if (dma64)
16203c4019f9SSowjanya Komatineni cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
16213c4019f9SSowjanya Komatineni
16223c4019f9SSowjanya Komatineni ret = cqhci_init(cq_host, host->mmc, dma64);
16233c4019f9SSowjanya Komatineni if (ret)
16243c4019f9SSowjanya Komatineni goto cleanup;
16253c4019f9SSowjanya Komatineni
16263c4019f9SSowjanya Komatineni ret = __sdhci_add_host(host);
16273c4019f9SSowjanya Komatineni if (ret)
16283c4019f9SSowjanya Komatineni goto cleanup;
16293c4019f9SSowjanya Komatineni
16303c4019f9SSowjanya Komatineni return 0;
16313c4019f9SSowjanya Komatineni
16323c4019f9SSowjanya Komatineni cleanup:
16333c4019f9SSowjanya Komatineni sdhci_cleanup_host(host);
16343c4019f9SSowjanya Komatineni return ret;
16353c4019f9SSowjanya Komatineni }
16363c4019f9SSowjanya Komatineni
163703813c81SPrathamesh Shete /* Program MC streamID for DMA transfers */
sdhci_tegra_program_stream_id(struct sdhci_host * host)163803813c81SPrathamesh Shete static void sdhci_tegra_program_stream_id(struct sdhci_host *host)
163903813c81SPrathamesh Shete {
164003813c81SPrathamesh Shete struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
164103813c81SPrathamesh Shete struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
164203813c81SPrathamesh Shete
164303813c81SPrathamesh Shete if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID) {
164403813c81SPrathamesh Shete tegra_sdhci_writel(host, FIELD_PREP(GENMASK(15, 8), tegra_host->stream_id) |
164503813c81SPrathamesh Shete FIELD_PREP(GENMASK(7, 0), tegra_host->stream_id),
164603813c81SPrathamesh Shete SDHCI_TEGRA_CIF2AXI_CTRL_0);
164703813c81SPrathamesh Shete }
164803813c81SPrathamesh Shete }
164903813c81SPrathamesh Shete
sdhci_tegra_probe(struct platform_device * pdev)1650c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
165103d2bfc8SOlof Johansson {
16523e44a1a7SStephen Warren const struct sdhci_tegra_soc_data *soc_data;
16533e44a1a7SStephen Warren struct sdhci_host *host;
165485d6509dSShawn Guo struct sdhci_pltfm_host *pltfm_host;
16553e44a1a7SStephen Warren struct sdhci_tegra *tegra_host;
165603d2bfc8SOlof Johansson struct clk *clk;
165703d2bfc8SOlof Johansson int rc;
165803d2bfc8SOlof Johansson
1659dfbaaec9SBean Huo soc_data = of_device_get_match_data(&pdev->dev);
1660dfbaaec9SBean Huo if (!soc_data)
1661b37f9d98SJoseph Lo return -EINVAL;
16623e44a1a7SStephen Warren
16630734e79cSJisheng Zhang host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
166485d6509dSShawn Guo if (IS_ERR(host))
166585d6509dSShawn Guo return PTR_ERR(host);
166685d6509dSShawn Guo pltfm_host = sdhci_priv(host);
166785d6509dSShawn Guo
16680734e79cSJisheng Zhang tegra_host = sdhci_pltfm_priv(pltfm_host);
1669a8e326a9SLucas Stach tegra_host->ddr_signaling = false;
1670e5c63d91SLucas Stach tegra_host->pad_calib_required = false;
167186ac2f8bSAapo Vienamo tegra_host->pad_control_available = false;
16723e44a1a7SStephen Warren tegra_host->soc_data = soc_data;
1673275173b2SGrant Likely
16741743fa54SDmitry Osipenko if (soc_data->nvquirks & NVQUIRK_HAS_ANDROID_GPT_SECTOR)
16751743fa54SDmitry Osipenko host->mmc->caps2 |= MMC_CAP2_ALT_GPT_TEGRA;
16761743fa54SDmitry Osipenko
167786ac2f8bSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
167886ac2f8bSAapo Vienamo rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
167986ac2f8bSAapo Vienamo if (rc == 0)
168086ac2f8bSAapo Vienamo host->mmc_host_ops.start_signal_voltage_switch =
168186ac2f8bSAapo Vienamo sdhci_tegra_start_signal_voltage_switch;
168286ac2f8bSAapo Vienamo }
168386ac2f8bSAapo Vienamo
168461dad40eSAapo Vienamo /* Hook to periodically rerun pad calibration */
168561dad40eSAapo Vienamo if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
168661dad40eSAapo Vienamo host->mmc_host_ops.request = tegra_sdhci_request;
168761dad40eSAapo Vienamo
1688dfc9700cSAapo Vienamo host->mmc_host_ops.hs400_enhanced_strobe =
1689dfc9700cSAapo Vienamo tegra_sdhci_hs400_enhanced_strobe;
1690dfc9700cSAapo Vienamo
1691ea8fc595SSowjanya Komatineni if (!host->ops->platform_execute_tuning)
1692ea8fc595SSowjanya Komatineni host->mmc_host_ops.execute_tuning =
1693ea8fc595SSowjanya Komatineni tegra_sdhci_execute_hw_tuning;
1694ea8fc595SSowjanya Komatineni
16952391b340SMylene JOSSERAND rc = mmc_of_parse(host->mmc);
169647caa84fSSimon Baatz if (rc)
169747caa84fSSimon Baatz goto err_parse_dt;
16980e786102SStephen Warren
16997ad2ed1dSLucas Stach if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1700c3c2384cSLucas Stach host->mmc->caps |= MMC_CAP_1_8V_DDR;
1701c3c2384cSLucas Stach
1702ff124c31SSowjanya Komatineni /* HW busy detection is supported, but R1B responses are required. */
1703ff124c31SSowjanya Komatineni host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
1704d2f8bfa4SUlf Hansson
170548f6daf1SAniruddha Rao /* GPIO CD can be set as a wakeup source */
170648f6daf1SAniruddha Rao host->mmc->caps |= MMC_CAP_CD_WAKE;
170748f6daf1SAniruddha Rao
17083c4019f9SSowjanya Komatineni tegra_sdhci_parse_dt(host);
170985c0da17SAapo Vienamo
171003813c81SPrathamesh Shete if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID &&
171103813c81SPrathamesh Shete !tegra_dev_iommu_get_stream_id(&pdev->dev, &tegra_host->stream_id)) {
171203813c81SPrathamesh Shete dev_warn(mmc_dev(host->mmc), "missing IOMMU stream ID\n");
171303813c81SPrathamesh Shete tegra_host->stream_id = 0x7f;
171403813c81SPrathamesh Shete }
171503813c81SPrathamesh Shete
17162391b340SMylene JOSSERAND tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
17172391b340SMylene JOSSERAND GPIOD_OUT_HIGH);
17182391b340SMylene JOSSERAND if (IS_ERR(tegra_host->power_gpio)) {
17192391b340SMylene JOSSERAND rc = PTR_ERR(tegra_host->power_gpio);
172085d6509dSShawn Guo goto err_power_req;
172103d2bfc8SOlof Johansson }
172203d2bfc8SOlof Johansson
17238048822bSSowjanya Komatineni /*
17248048822bSSowjanya Komatineni * Tegra210 has a separate SDMMC_LEGACY_TM clock used for host
17258048822bSSowjanya Komatineni * timeout clock and SW can choose TMCLK or SDCLK for hardware
17268048822bSSowjanya Komatineni * data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT of
17278048822bSSowjanya Komatineni * the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL.
17288048822bSSowjanya Komatineni *
17298048822bSSowjanya Komatineni * USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses
17308048822bSSowjanya Komatineni * 12Mhz TMCLK which is advertised in host capability register.
17318048822bSSowjanya Komatineni * With TMCLK of 12Mhz provides maximum data timeout period that can
17328048822bSSowjanya Komatineni * be achieved is 11s better than using SDCLK for data timeout.
17338048822bSSowjanya Komatineni *
17348048822bSSowjanya Komatineni * So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's
17358048822bSSowjanya Komatineni * supporting separate TMCLK.
17368048822bSSowjanya Komatineni */
17378048822bSSowjanya Komatineni
17388048822bSSowjanya Komatineni if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) {
17398048822bSSowjanya Komatineni clk = devm_clk_get(&pdev->dev, "tmclk");
17408048822bSSowjanya Komatineni if (IS_ERR(clk)) {
17418048822bSSowjanya Komatineni rc = PTR_ERR(clk);
17428048822bSSowjanya Komatineni if (rc == -EPROBE_DEFER)
17438048822bSSowjanya Komatineni goto err_power_req;
17448048822bSSowjanya Komatineni
17458048822bSSowjanya Komatineni dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc);
17468048822bSSowjanya Komatineni clk = NULL;
17478048822bSSowjanya Komatineni }
17488048822bSSowjanya Komatineni
17498048822bSSowjanya Komatineni clk_set_rate(clk, 12000000);
17508048822bSSowjanya Komatineni rc = clk_prepare_enable(clk);
17518048822bSSowjanya Komatineni if (rc) {
17528048822bSSowjanya Komatineni dev_err(&pdev->dev,
17538048822bSSowjanya Komatineni "failed to enable tmclk: %d\n", rc);
17548048822bSSowjanya Komatineni goto err_power_req;
17558048822bSSowjanya Komatineni }
17568048822bSSowjanya Komatineni
17578048822bSSowjanya Komatineni tegra_host->tmclk = clk;
17588048822bSSowjanya Komatineni }
17598048822bSSowjanya Komatineni
1760e4f79d9cSKevin Hao clk = devm_clk_get(mmc_dev(host->mmc), NULL);
176103d2bfc8SOlof Johansson if (IS_ERR(clk)) {
1762180a4665SKrzysztof Kozlowski rc = dev_err_probe(&pdev->dev, PTR_ERR(clk),
1763180a4665SKrzysztof Kozlowski "failed to get clock\n");
176485d6509dSShawn Guo goto err_clk_get;
176503d2bfc8SOlof Johansson }
176603d2bfc8SOlof Johansson pltfm_host->clk = clk;
176703d2bfc8SOlof Johansson
17682cd6c49dSPhilipp Zabel tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
17692cd6c49dSPhilipp Zabel "sdhci");
177020567be9SThierry Reding if (IS_ERR(tegra_host->rst)) {
177120567be9SThierry Reding rc = PTR_ERR(tegra_host->rst);
177220567be9SThierry Reding dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
177320567be9SThierry Reding goto err_rst_get;
177420567be9SThierry Reding }
177520567be9SThierry Reding
1776d618978dSDmitry Osipenko rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
177720567be9SThierry Reding if (rc)
177820567be9SThierry Reding goto err_rst_get;
177920567be9SThierry Reding
1780d618978dSDmitry Osipenko pm_runtime_enable(&pdev->dev);
1781d618978dSDmitry Osipenko rc = pm_runtime_resume_and_get(&pdev->dev);
1782d618978dSDmitry Osipenko if (rc)
1783d618978dSDmitry Osipenko goto err_pm_get;
1784d618978dSDmitry Osipenko
1785d618978dSDmitry Osipenko rc = reset_control_assert(tegra_host->rst);
1786d618978dSDmitry Osipenko if (rc)
1787d618978dSDmitry Osipenko goto err_rst_assert;
1788d618978dSDmitry Osipenko
178920567be9SThierry Reding usleep_range(2000, 4000);
179020567be9SThierry Reding
179120567be9SThierry Reding rc = reset_control_deassert(tegra_host->rst);
179220567be9SThierry Reding if (rc)
1793d618978dSDmitry Osipenko goto err_rst_assert;
179420567be9SThierry Reding
179520567be9SThierry Reding usleep_range(2000, 4000);
179620567be9SThierry Reding
17973c4019f9SSowjanya Komatineni rc = sdhci_tegra_add_host(host);
179885d6509dSShawn Guo if (rc)
179985d6509dSShawn Guo goto err_add_host;
180085d6509dSShawn Guo
180103813c81SPrathamesh Shete sdhci_tegra_program_stream_id(host);
180203813c81SPrathamesh Shete
180303d2bfc8SOlof Johansson return 0;
180403d2bfc8SOlof Johansson
180585d6509dSShawn Guo err_add_host:
180620567be9SThierry Reding reset_control_assert(tegra_host->rst);
1807d618978dSDmitry Osipenko err_rst_assert:
1808d618978dSDmitry Osipenko pm_runtime_put_sync_suspend(&pdev->dev);
1809d618978dSDmitry Osipenko err_pm_get:
1810d618978dSDmitry Osipenko pm_runtime_disable(&pdev->dev);
181120567be9SThierry Reding err_rst_get:
181285d6509dSShawn Guo err_clk_get:
18138048822bSSowjanya Komatineni clk_disable_unprepare(tegra_host->tmclk);
181485d6509dSShawn Guo err_power_req:
181547caa84fSSimon Baatz err_parse_dt:
181685d6509dSShawn Guo sdhci_pltfm_free(pdev);
181703d2bfc8SOlof Johansson return rc;
181803d2bfc8SOlof Johansson }
181903d2bfc8SOlof Johansson
sdhci_tegra_remove(struct platform_device * pdev)1820*8d9b1788SYangtao Li static void sdhci_tegra_remove(struct platform_device *pdev)
182120567be9SThierry Reding {
182220567be9SThierry Reding struct sdhci_host *host = platform_get_drvdata(pdev);
182320567be9SThierry Reding struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
182420567be9SThierry Reding struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
182520567be9SThierry Reding
182620567be9SThierry Reding sdhci_remove_host(host, 0);
182720567be9SThierry Reding
182820567be9SThierry Reding reset_control_assert(tegra_host->rst);
182920567be9SThierry Reding usleep_range(2000, 4000);
183020567be9SThierry Reding
1831d618978dSDmitry Osipenko pm_runtime_put_sync_suspend(&pdev->dev);
1832d618978dSDmitry Osipenko pm_runtime_force_suspend(&pdev->dev);
1833d618978dSDmitry Osipenko
1834d618978dSDmitry Osipenko clk_disable_unprepare(tegra_host->tmclk);
183520567be9SThierry Reding sdhci_pltfm_free(pdev);
183620567be9SThierry Reding }
183720567be9SThierry Reding
sdhci_tegra_runtime_suspend(struct device * dev)1838d618978dSDmitry Osipenko static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
183971c733c4SSowjanya Komatineni {
184071c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev);
184171c733c4SSowjanya Komatineni struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1842d618978dSDmitry Osipenko
1843d618978dSDmitry Osipenko clk_disable_unprepare(pltfm_host->clk);
1844d618978dSDmitry Osipenko
1845d618978dSDmitry Osipenko return 0;
1846d618978dSDmitry Osipenko }
1847d618978dSDmitry Osipenko
sdhci_tegra_runtime_resume(struct device * dev)1848d618978dSDmitry Osipenko static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
1849d618978dSDmitry Osipenko {
1850d618978dSDmitry Osipenko struct sdhci_host *host = dev_get_drvdata(dev);
1851d618978dSDmitry Osipenko struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1852d618978dSDmitry Osipenko
1853d618978dSDmitry Osipenko return clk_prepare_enable(pltfm_host->clk);
1854d618978dSDmitry Osipenko }
1855d618978dSDmitry Osipenko
1856d618978dSDmitry Osipenko #ifdef CONFIG_PM_SLEEP
sdhci_tegra_suspend(struct device * dev)1857d618978dSDmitry Osipenko static int sdhci_tegra_suspend(struct device *dev)
1858d618978dSDmitry Osipenko {
1859d618978dSDmitry Osipenko struct sdhci_host *host = dev_get_drvdata(dev);
186071c733c4SSowjanya Komatineni int ret;
186171c733c4SSowjanya Komatineni
186271c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) {
186371c733c4SSowjanya Komatineni ret = cqhci_suspend(host->mmc);
186471c733c4SSowjanya Komatineni if (ret)
186571c733c4SSowjanya Komatineni return ret;
186671c733c4SSowjanya Komatineni }
186771c733c4SSowjanya Komatineni
186871c733c4SSowjanya Komatineni ret = sdhci_suspend_host(host);
186971c733c4SSowjanya Komatineni if (ret) {
187071c733c4SSowjanya Komatineni cqhci_resume(host->mmc);
187171c733c4SSowjanya Komatineni return ret;
187271c733c4SSowjanya Komatineni }
187371c733c4SSowjanya Komatineni
1874d618978dSDmitry Osipenko ret = pm_runtime_force_suspend(dev);
1875d618978dSDmitry Osipenko if (ret) {
1876d618978dSDmitry Osipenko sdhci_resume_host(host);
1877d618978dSDmitry Osipenko cqhci_resume(host->mmc);
1878d618978dSDmitry Osipenko return ret;
1879d618978dSDmitry Osipenko }
1880d618978dSDmitry Osipenko
188148f6daf1SAniruddha Rao return mmc_gpio_set_cd_wake(host->mmc, true);
188271c733c4SSowjanya Komatineni }
188371c733c4SSowjanya Komatineni
sdhci_tegra_resume(struct device * dev)1884d618978dSDmitry Osipenko static int sdhci_tegra_resume(struct device *dev)
188571c733c4SSowjanya Komatineni {
188671c733c4SSowjanya Komatineni struct sdhci_host *host = dev_get_drvdata(dev);
188771c733c4SSowjanya Komatineni int ret;
188871c733c4SSowjanya Komatineni
188948f6daf1SAniruddha Rao ret = mmc_gpio_set_cd_wake(host->mmc, false);
189048f6daf1SAniruddha Rao if (ret)
189148f6daf1SAniruddha Rao return ret;
189248f6daf1SAniruddha Rao
1893d618978dSDmitry Osipenko ret = pm_runtime_force_resume(dev);
189471c733c4SSowjanya Komatineni if (ret)
189571c733c4SSowjanya Komatineni return ret;
189671c733c4SSowjanya Komatineni
189703813c81SPrathamesh Shete sdhci_tegra_program_stream_id(host);
189803813c81SPrathamesh Shete
189971c733c4SSowjanya Komatineni ret = sdhci_resume_host(host);
190071c733c4SSowjanya Komatineni if (ret)
190171c733c4SSowjanya Komatineni goto disable_clk;
190271c733c4SSowjanya Komatineni
190371c733c4SSowjanya Komatineni if (host->mmc->caps2 & MMC_CAP2_CQE) {
190471c733c4SSowjanya Komatineni ret = cqhci_resume(host->mmc);
190571c733c4SSowjanya Komatineni if (ret)
190671c733c4SSowjanya Komatineni goto suspend_host;
190771c733c4SSowjanya Komatineni }
190871c733c4SSowjanya Komatineni
190971c733c4SSowjanya Komatineni return 0;
191071c733c4SSowjanya Komatineni
191171c733c4SSowjanya Komatineni suspend_host:
191271c733c4SSowjanya Komatineni sdhci_suspend_host(host);
191371c733c4SSowjanya Komatineni disable_clk:
1914d618978dSDmitry Osipenko pm_runtime_force_suspend(dev);
191571c733c4SSowjanya Komatineni return ret;
191671c733c4SSowjanya Komatineni }
191771c733c4SSowjanya Komatineni #endif
191871c733c4SSowjanya Komatineni
1919d618978dSDmitry Osipenko static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
1920d618978dSDmitry Osipenko SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
1921d618978dSDmitry Osipenko NULL)
1922d618978dSDmitry Osipenko SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
1923d618978dSDmitry Osipenko };
192471c733c4SSowjanya Komatineni
192585d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
192685d6509dSShawn Guo .driver = {
192785d6509dSShawn Guo .name = "sdhci-tegra",
192821b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
1929275173b2SGrant Likely .of_match_table = sdhci_tegra_dt_match,
193071c733c4SSowjanya Komatineni .pm = &sdhci_tegra_dev_pm_ops,
193185d6509dSShawn Guo },
193285d6509dSShawn Guo .probe = sdhci_tegra_probe,
1933*8d9b1788SYangtao Li .remove_new = sdhci_tegra_remove,
193403d2bfc8SOlof Johansson };
193503d2bfc8SOlof Johansson
1936d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
193785d6509dSShawn Guo
193885d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
193985d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
194085d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1941