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 SPACEMIT_SDHC_MMC_CTRL_REG 0x114 24 #define SDHC_MISC_INT_EN BIT(1) 25 #define SDHC_MISC_INT BIT(2) 26 #define SDHC_ENHANCE_STROBE_EN BIT(8) 27 #define SDHC_MMC_HS400 BIT(9) 28 #define SDHC_MMC_HS200 BIT(10) 29 #define SDHC_MMC_CARD_MODE BIT(12) 30 31 #define SPACEMIT_SDHC_TX_CFG_REG 0x11C 32 #define SDHC_TX_INT_CLK_SEL BIT(30) 33 #define SDHC_TX_MUX_SEL BIT(31) 34 35 #define SPACEMIT_SDHC_PHY_CTRL_REG 0x160 36 #define SDHC_PHY_FUNC_EN BIT(0) 37 #define SDHC_PHY_PLL_LOCK BIT(1) 38 #define SDHC_HOST_LEGACY_MODE BIT(31) 39 40 #define SPACEMIT_SDHC_PHY_FUNC_REG 0x164 41 #define SDHC_PHY_TEST_EN BIT(7) 42 #define SDHC_HS200_USE_RFIFO BIT(15) 43 44 #define SPACEMIT_SDHC_PHY_DLLCFG 0x168 45 #define SDHC_DLL_PREDLY_NUM GENMASK(3, 2) 46 #define SDHC_DLL_FULLDLY_RANGE GENMASK(5, 4) 47 #define SDHC_DLL_VREG_CTRL GENMASK(7, 6) 48 #define SDHC_DLL_ENABLE BIT(31) 49 50 #define SPACEMIT_SDHC_PHY_DLLCFG1 0x16C 51 #define SDHC_DLL_REG1_CTRL GENMASK(7, 0) 52 #define SDHC_DLL_REG2_CTRL GENMASK(15, 8) 53 #define SDHC_DLL_REG3_CTRL GENMASK(23, 16) 54 #define SDHC_DLL_REG4_CTRL GENMASK(31, 24) 55 56 #define SPACEMIT_SDHC_PHY_DLLSTS 0x170 57 #define SDHC_DLL_LOCK_STATE BIT(0) 58 59 #define SPACEMIT_SDHC_PHY_PADCFG_REG 0x178 60 #define SDHC_PHY_DRIVE_SEL GENMASK(2, 0) 61 #define SDHC_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, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, 95 SPACEMIT_SDHC_PHY_CTRL_REG); 96 97 spacemit_sdhci_clrsetbits(host, SDHC_PHY_DRIVE_SEL, 98 SDHC_RX_BIAS_CTRL | FIELD_PREP(SDHC_PHY_DRIVE_SEL, 4), 99 SPACEMIT_SDHC_PHY_PADCFG_REG); 100 101 if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) 102 spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG); 103 } 104 105 static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) 106 { 107 if (timing == MMC_TIMING_MMC_HS200) 108 spacemit_sdhci_setbits(host, SDHC_MMC_HS200, SPACEMIT_SDHC_MMC_CTRL_REG); 109 110 if (timing == MMC_TIMING_MMC_HS400) 111 spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); 112 113 sdhci_set_uhs_signaling(host, timing); 114 115 if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO)) 116 spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2); 117 } 118 119 static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) 120 { 121 struct mmc_host *mmc = host->mmc; 122 123 if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50) 124 spacemit_sdhci_setbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); 125 else 126 spacemit_sdhci_clrbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); 127 128 sdhci_set_clock(host, clock); 129 }; 130 131 static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host) 132 { 133 u32 state; 134 int ret; 135 136 spacemit_sdhci_clrsetbits(host, SDHC_DLL_PREDLY_NUM | 137 SDHC_DLL_FULLDLY_RANGE | 138 SDHC_DLL_VREG_CTRL, 139 FIELD_PREP(SDHC_DLL_PREDLY_NUM, 1) | 140 FIELD_PREP(SDHC_DLL_FULLDLY_RANGE, 1) | 141 FIELD_PREP(SDHC_DLL_VREG_CTRL, 1), 142 SPACEMIT_SDHC_PHY_DLLCFG); 143 144 spacemit_sdhci_clrsetbits(host, SDHC_DLL_REG1_CTRL, 145 FIELD_PREP(SDHC_DLL_REG1_CTRL, 0x92), 146 SPACEMIT_SDHC_PHY_DLLCFG1); 147 148 spacemit_sdhci_setbits(host, SDHC_DLL_ENABLE, SPACEMIT_SDHC_PHY_DLLCFG); 149 150 ret = readl_poll_timeout(host->ioaddr + SPACEMIT_SDHC_PHY_DLLSTS, state, 151 state & SDHC_DLL_LOCK_STATE, 2, 100); 152 if (ret == -ETIMEDOUT) 153 dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n"); 154 } 155 156 static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) 157 { 158 struct sdhci_host *host = mmc_priv(mmc); 159 160 if (!ios->enhanced_strobe) { 161 spacemit_sdhci_clrbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); 162 return; 163 } 164 165 spacemit_sdhci_setbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); 166 spacemit_sdhci_phy_dll_init(host); 167 } 168 169 static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host) 170 { 171 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 172 173 return clk_get_rate(pltfm_host->clk); 174 } 175 176 static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) 177 { 178 struct sdhci_host *host = mmc_priv(mmc); 179 180 spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); 181 182 return 0; 183 } 184 185 static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc) 186 { 187 struct sdhci_host *host = mmc_priv(mmc); 188 189 spacemit_sdhci_phy_dll_init(host); 190 } 191 192 static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) 193 { 194 struct sdhci_host *host = mmc_priv(mmc); 195 196 spacemit_sdhci_clrbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, 197 SPACEMIT_SDHC_PHY_CTRL_REG); 198 spacemit_sdhci_clrbits(host, SDHC_MMC_HS400 | SDHC_MMC_HS200 | SDHC_ENHANCE_STROBE_EN, 199 SPACEMIT_SDHC_MMC_CTRL_REG); 200 spacemit_sdhci_clrbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG); 201 202 udelay(5); 203 204 spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, 205 SPACEMIT_SDHC_PHY_CTRL_REG); 206 } 207 208 static inline int spacemit_sdhci_get_clocks(struct device *dev, 209 struct sdhci_pltfm_host *pltfm_host) 210 { 211 struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); 212 213 sdhst->clk_core = devm_clk_get_enabled(dev, "core"); 214 if (IS_ERR(sdhst->clk_core)) 215 return -EINVAL; 216 217 sdhst->clk_io = devm_clk_get_enabled(dev, "io"); 218 if (IS_ERR(sdhst->clk_io)) 219 return -EINVAL; 220 221 pltfm_host->clk = sdhst->clk_io; 222 223 return 0; 224 } 225 226 static const struct sdhci_ops spacemit_sdhci_ops = { 227 .get_max_clock = spacemit_sdhci_clk_get_max_clock, 228 .reset = spacemit_sdhci_reset, 229 .set_bus_width = sdhci_set_bus_width, 230 .set_clock = spacemit_sdhci_set_clock, 231 .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling, 232 }; 233 234 static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { 235 .ops = &spacemit_sdhci_ops, 236 .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 237 SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 238 SDHCI_QUIRK_32BIT_ADMA_SIZE | 239 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 240 SDHCI_QUIRK_BROKEN_CARD_DETECTION | 241 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, 242 .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA | 243 SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 244 }; 245 246 static const struct of_device_id spacemit_sdhci_of_match[] = { 247 { .compatible = "spacemit,k1-sdhci" }, 248 { /* sentinel */ } 249 }; 250 MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); 251 252 static int spacemit_sdhci_probe(struct platform_device *pdev) 253 { 254 struct device *dev = &pdev->dev; 255 struct spacemit_sdhci_host *sdhst; 256 struct sdhci_pltfm_host *pltfm_host; 257 struct sdhci_host *host; 258 struct mmc_host_ops *mops; 259 int ret; 260 261 host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst)); 262 if (IS_ERR(host)) 263 return PTR_ERR(host); 264 265 pltfm_host = sdhci_priv(host); 266 267 ret = mmc_of_parse(host->mmc); 268 if (ret) 269 goto err_pltfm; 270 271 sdhci_get_of_property(pdev); 272 273 if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) { 274 mops = &host->mmc_host_ops; 275 mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400; 276 mops->hs400_complete = spacemit_sdhci_post_select_hs400; 277 mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200; 278 mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe; 279 } 280 281 host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; 282 283 ret = spacemit_sdhci_get_clocks(dev, pltfm_host); 284 if (ret) 285 goto err_pltfm; 286 287 ret = sdhci_add_host(host); 288 if (ret) 289 goto err_pltfm; 290 291 return 0; 292 293 err_pltfm: 294 return ret; 295 } 296 297 static struct platform_driver spacemit_sdhci_driver = { 298 .driver = { 299 .name = "sdhci-spacemit", 300 .of_match_table = spacemit_sdhci_of_match, 301 }, 302 .probe = spacemit_sdhci_probe, 303 .remove = sdhci_pltfm_remove, 304 }; 305 module_platform_driver(spacemit_sdhci_driver); 306 307 MODULE_DESCRIPTION("SpacemiT SDHCI platform driver"); 308 MODULE_LICENSE("GPL"); 309