Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.17-rc4 308 lines 8.7 kB view raw
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 63struct 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 */ 69static 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 74static 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 79static 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 87static 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 105static 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 119static 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 131static 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 156static 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 169static 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 176static 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 185static 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 192static 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 208static 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 226static 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 234static 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 246static const struct of_device_id spacemit_sdhci_of_match[] = { 247 { .compatible = "spacemit,k1-sdhci" }, 248 { /* sentinel */ } 249}; 250MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); 251 252static 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 293err_pltfm: 294 return ret; 295} 296 297static 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}; 305module_platform_driver(spacemit_sdhci_driver); 306 307MODULE_DESCRIPTION("SpacemiT SDHCI platform driver"); 308MODULE_LICENSE("GPL");