1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd 4 * Copyright (c) 2025 Yixun Lan <dlan@gentoo.org> 5 */ 6 7 #include <linux/bitfield.h> 8 #include <linux/clk.h> 9 #include <linux/delay.h> 10 #include <linux/iopoll.h> 11 #include <linux/init.h> 12 #include <linux/mmc/card.h> 13 #include <linux/mmc/host.h> 14 #include <linux/mmc/mmc.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/of_device.h> 18 #include <linux/platform_device.h> 19 20 #include "sdhci.h" 21 #include "sdhci-pltfm.h" 22 23 #define SDHC_MMC_CTRL_REG 0x114 24 #define MISC_INT_EN BIT(1) 25 #define MISC_INT BIT(2) 26 #define ENHANCE_STROBE_EN BIT(8) 27 #define MMC_HS400 BIT(9) 28 #define MMC_HS200 BIT(10) 29 #define MMC_CARD_MODE BIT(12) 30 31 #define SDHC_TX_CFG_REG 0x11C 32 #define TX_INT_CLK_SEL BIT(30) 33 #define TX_MUX_SEL BIT(31) 34 35 #define SDHC_PHY_CTRL_REG 0x160 36 #define PHY_FUNC_EN BIT(0) 37 #define PHY_PLL_LOCK BIT(1) 38 #define HOST_LEGACY_MODE BIT(31) 39 40 #define SDHC_PHY_FUNC_REG 0x164 41 #define PHY_TEST_EN BIT(7) 42 #define HS200_USE_RFIFO BIT(15) 43 44 #define SDHC_PHY_DLLCFG 0x168 45 #define DLL_PREDLY_NUM GENMASK(3, 2) 46 #define DLL_FULLDLY_RANGE GENMASK(5, 4) 47 #define DLL_VREG_CTRL GENMASK(7, 6) 48 #define DLL_ENABLE BIT(31) 49 50 #define SDHC_PHY_DLLCFG1 0x16C 51 #define DLL_REG1_CTRL GENMASK(7, 0) 52 #define DLL_REG2_CTRL GENMASK(15, 8) 53 #define DLL_REG3_CTRL GENMASK(23, 16) 54 #define DLL_REG4_CTRL GENMASK(31, 24) 55 56 #define SDHC_PHY_DLLSTS 0x170 57 #define DLL_LOCK_STATE BIT(0) 58 59 #define SDHC_PHY_PADCFG_REG 0x178 60 #define PHY_DRIVE_SEL GENMASK(2, 0) 61 #define RX_BIAS_CTRL BIT(5) 62 63 struct spacemit_sdhci_host { 64 struct clk *clk_core; 65 struct clk *clk_io; 66 }; 67 68 /* All helper functions will update clr/set while preserve rest bits */ 69 static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg) 70 { 71 sdhci_writel(host, sdhci_readl(host, reg) | val, reg); 72 } 73 74 static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg) 75 { 76 sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg); 77 } 78 79 static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg) 80 { 81 u32 val = sdhci_readl(host, reg); 82 83 val = (val & ~clr) | set; 84 sdhci_writel(host, val, reg); 85 } 86 87 static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) 88 { 89 sdhci_reset(host, mask); 90 91 if (mask != SDHCI_RESET_ALL) 92 return; 93 94 spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); 95 96 spacemit_sdhci_clrsetbits(host, PHY_DRIVE_SEL, 97 RX_BIAS_CTRL | FIELD_PREP(PHY_DRIVE_SEL, 4), 98 SDHC_PHY_PADCFG_REG); 99 100 if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) 101 spacemit_sdhci_setbits(host, MMC_CARD_MODE, SDHC_MMC_CTRL_REG); 102 } 103 104 static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) 105 { 106 if (timing == MMC_TIMING_MMC_HS200) 107 spacemit_sdhci_setbits(host, MMC_HS200, SDHC_MMC_CTRL_REG); 108 109 if (timing == MMC_TIMING_MMC_HS400) 110 spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG); 111 112 sdhci_set_uhs_signaling(host, timing); 113 114 if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO)) 115 spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2); 116 } 117 118 static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 119 { 120 struct mmc_host *mmc = host->mmc; 121 122 if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50) 123 spacemit_sdhci_setbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG); 124 else 125 spacemit_sdhci_clrbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG); 126 127 sdhci_set_clock(host, clock); 128 }; 129 130 static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host) 131 { 132 u32 state; 133 int ret; 134 135 spacemit_sdhci_clrsetbits(host, DLL_PREDLY_NUM | DLL_FULLDLY_RANGE | DLL_VREG_CTRL, 136 FIELD_PREP(DLL_PREDLY_NUM, 1) | 137 FIELD_PREP(DLL_FULLDLY_RANGE, 1) | 138 FIELD_PREP(DLL_VREG_CTRL, 1), 139 SDHC_PHY_DLLCFG); 140 141 spacemit_sdhci_clrsetbits(host, DLL_REG1_CTRL, 142 FIELD_PREP(DLL_REG1_CTRL, 0x92), 143 SDHC_PHY_DLLCFG1); 144 145 spacemit_sdhci_setbits(host, DLL_ENABLE, SDHC_PHY_DLLCFG); 146 147 ret = readl_poll_timeout(host->ioaddr + SDHC_PHY_DLLSTS, state, 148 state & DLL_LOCK_STATE, 2, 100); 149 if (ret == -ETIMEDOUT) 150 dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n"); 151 } 152 153 static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) 154 { 155 struct sdhci_host *host = mmc_priv(mmc); 156 157 if (!ios->enhanced_strobe) { 158 spacemit_sdhci_clrbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); 159 return; 160 } 161 162 spacemit_sdhci_setbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); 163 spacemit_sdhci_phy_dll_init(host); 164 } 165 166 static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host) 167 { 168 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 169 170 return clk_get_rate(pltfm_host->clk); 171 } 172 173 static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) 174 { 175 struct sdhci_host *host = mmc_priv(mmc); 176 177 spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG); 178 host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 179 180 return 0; 181 } 182 183 static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc) 184 { 185 struct sdhci_host *host = mmc_priv(mmc); 186 187 spacemit_sdhci_phy_dll_init(host); 188 host->mmc->caps &= ~MMC_CAP_WAIT_WHILE_BUSY; 189 } 190 191 static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) 192 { 193 struct sdhci_host *host = mmc_priv(mmc); 194 195 spacemit_sdhci_clrbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); 196 spacemit_sdhci_clrbits(host, MMC_HS400 | MMC_HS200 | ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG); 197 spacemit_sdhci_clrbits(host, HS200_USE_RFIFO, SDHC_PHY_FUNC_REG); 198 199 udelay(5); 200 201 spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG); 202 } 203 204 static inline int spacemit_sdhci_get_clocks(struct device *dev, 205 struct sdhci_pltfm_host *pltfm_host) 206 { 207 struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); 208 209 sdhst->clk_core = devm_clk_get_enabled(dev, "core"); 210 if (IS_ERR(sdhst->clk_core)) 211 return -EINVAL; 212 213 sdhst->clk_io = devm_clk_get_enabled(dev, "io"); 214 if (IS_ERR(sdhst->clk_io)) 215 return -EINVAL; 216 217 pltfm_host->clk = sdhst->clk_io; 218 219 return 0; 220 } 221 222 static const struct sdhci_ops spacemit_sdhci_ops = { 223 .get_max_clock = spacemit_sdhci_clk_get_max_clock, 224 .reset = spacemit_sdhci_reset, 225 .set_bus_width = sdhci_set_bus_width, 226 .set_clock = spacemit_sdhci_set_clock, 227 .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling, 228 }; 229 230 static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { 231 .ops = &spacemit_sdhci_ops, 232 .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 233 SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 234 SDHCI_QUIRK_32BIT_ADMA_SIZE | 235 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 236 SDHCI_QUIRK_BROKEN_CARD_DETECTION | 237 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, 238 .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA | 239 SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 240 }; 241 242 static const struct of_device_id spacemit_sdhci_of_match[] = { 243 { .compatible = "spacemit,k1-sdhci" }, 244 { /* sentinel */ } 245 }; 246 MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); 247 248 static int spacemit_sdhci_probe(struct platform_device *pdev) 249 { 250 struct device *dev = &pdev->dev; 251 struct spacemit_sdhci_host *sdhst; 252 struct sdhci_pltfm_host *pltfm_host; 253 struct sdhci_host *host; 254 struct mmc_host_ops *mops; 255 int ret; 256 257 host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst)); 258 if (IS_ERR(host)) 259 return PTR_ERR(host); 260 261 pltfm_host = sdhci_priv(host); 262 263 ret = mmc_of_parse(host->mmc); 264 if (ret) 265 goto err_pltfm; 266 267 sdhci_get_of_property(pdev); 268 269 if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) { 270 mops = &host->mmc_host_ops; 271 mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400; 272 mops->hs400_complete = spacemit_sdhci_post_select_hs400; 273 mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200; 274 mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe; 275 } 276 277 host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; 278 279 if (spacemit_sdhci_get_clocks(dev, pltfm_host)) 280 goto err_pltfm; 281 282 ret = sdhci_add_host(host); 283 if (ret) 284 goto err_pltfm; 285 286 return 0; 287 288 err_pltfm: 289 sdhci_pltfm_free(pdev); 290 return ret; 291 } 292 293 static struct platform_driver spacemit_sdhci_driver = { 294 .driver = { 295 .name = "sdhci-spacemit", 296 .of_match_table = spacemit_sdhci_of_match, 297 }, 298 .probe = spacemit_sdhci_probe, 299 .remove = sdhci_pltfm_remove, 300 }; 301 module_platform_driver(spacemit_sdhci_driver); 302 303 MODULE_DESCRIPTION("SpacemiT SDHCI platform driver"); 304 MODULE_LICENSE("GPL"); 305