1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 Socionext Inc. 4 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 5 */ 6 7 #include <linux/bitfield.h> 8 #include <linux/bits.h> 9 #include <linux/iopoll.h> 10 #include <linux/module.h> 11 #include <linux/mmc/host.h> 12 #include <linux/mmc/mmc.h> 13 #include <linux/of.h> 14 15 #include "sdhci-pltfm.h" 16 17 /* HRS - Host Register Set (specific to Cadence) */ 18 #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ 19 #define SDHCI_CDNS_HRS04_ACK BIT(26) 20 #define SDHCI_CDNS_HRS04_RD BIT(25) 21 #define SDHCI_CDNS_HRS04_WR BIT(24) 22 #define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16) 23 #define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8) 24 #define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0) 25 26 #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */ 27 #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15) 28 #define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8) 29 #define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0) 30 #define SDHCI_CDNS_HRS06_MODE_SD 0x0 31 #define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2 32 #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3 33 #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4 34 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 35 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 36 37 /* SRS - Slot Register Set (SDHCI-compatible) */ 38 #define SDHCI_CDNS_SRS_BASE 0x200 39 40 /* PHY */ 41 #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00 42 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01 43 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02 44 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03 45 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04 46 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05 47 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06 48 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07 49 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08 50 #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b 51 #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c 52 #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d 53 54 /* 55 * The tuned val register is 6 bit-wide, but not the whole of the range is 56 * available. The range 0-42 seems to be available (then 43 wraps around to 0) 57 * but I am not quite sure if it is official. Use only 0 to 39 for safety. 58 */ 59 #define SDHCI_CDNS_MAX_TUNING_LOOP 40 60 61 struct sdhci_cdns_phy_param { 62 u8 addr; 63 u8 data; 64 }; 65 66 struct sdhci_cdns_priv { 67 void __iomem *hrs_addr; 68 bool enhanced_strobe; 69 unsigned int nr_phy_params; 70 struct sdhci_cdns_phy_param phy_params[0]; 71 }; 72 73 struct sdhci_cdns_phy_cfg { 74 const char *property; 75 u8 addr; 76 }; 77 78 static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { 79 { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, }, 80 { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, }, 81 { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, }, 82 { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, }, 83 { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, }, 84 { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, }, 85 { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, }, 86 { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, }, 87 { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, }, 88 { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, }, 89 { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, }, 90 }; 91 92 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, 93 u8 addr, u8 data) 94 { 95 void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04; 96 u32 tmp; 97 int ret; 98 99 tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) | 100 FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr); 101 writel(tmp, reg); 102 103 tmp |= SDHCI_CDNS_HRS04_WR; 104 writel(tmp, reg); 105 106 ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10); 107 if (ret) 108 return ret; 109 110 tmp &= ~SDHCI_CDNS_HRS04_WR; 111 writel(tmp, reg); 112 113 return 0; 114 } 115 116 static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) 117 { 118 unsigned int count = 0; 119 int i; 120 121 for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) 122 if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property)) 123 count++; 124 125 return count; 126 } 127 128 static void sdhci_cdns_phy_param_parse(struct device_node *np, 129 struct sdhci_cdns_priv *priv) 130 { 131 struct sdhci_cdns_phy_param *p = priv->phy_params; 132 u32 val; 133 int ret, i; 134 135 for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) { 136 ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property, 137 &val); 138 if (ret) 139 continue; 140 141 p->addr = sdhci_cdns_phy_cfgs[i].addr; 142 p->data = val; 143 p++; 144 } 145 } 146 147 static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv) 148 { 149 int ret, i; 150 151 for (i = 0; i < priv->nr_phy_params; i++) { 152 ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr, 153 priv->phy_params[i].data); 154 if (ret) 155 return ret; 156 } 157 158 return 0; 159 } 160 161 static inline void *sdhci_cdns_priv(struct sdhci_host *host) 162 { 163 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 164 165 return sdhci_pltfm_priv(pltfm_host); 166 } 167 168 static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host) 169 { 170 /* 171 * Cadence's spec says the Timeout Clock Frequency is the same as the 172 * Base Clock Frequency. 173 */ 174 return host->max_clk; 175 } 176 177 static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode) 178 { 179 u32 tmp; 180 181 /* The speed mode for eMMC is selected by HRS06 register */ 182 tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); 183 tmp &= ~SDHCI_CDNS_HRS06_MODE; 184 tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); 185 writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); 186 } 187 188 static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv) 189 { 190 u32 tmp; 191 192 tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); 193 return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp); 194 } 195 196 static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, 197 unsigned int timing) 198 { 199 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); 200 u32 mode; 201 202 switch (timing) { 203 case MMC_TIMING_MMC_HS: 204 mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; 205 break; 206 case MMC_TIMING_MMC_DDR52: 207 mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; 208 break; 209 case MMC_TIMING_MMC_HS200: 210 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; 211 break; 212 case MMC_TIMING_MMC_HS400: 213 if (priv->enhanced_strobe) 214 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES; 215 else 216 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; 217 break; 218 default: 219 mode = SDHCI_CDNS_HRS06_MODE_SD; 220 break; 221 } 222 223 sdhci_cdns_set_emmc_mode(priv, mode); 224 225 /* For SD, fall back to the default handler */ 226 if (mode == SDHCI_CDNS_HRS06_MODE_SD) 227 sdhci_set_uhs_signaling(host, timing); 228 } 229 230 static const struct sdhci_ops sdhci_cdns_ops = { 231 .set_clock = sdhci_set_clock, 232 .get_timeout_clock = sdhci_cdns_get_timeout_clock, 233 .set_bus_width = sdhci_set_bus_width, 234 .reset = sdhci_reset, 235 .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, 236 }; 237 238 static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = { 239 .ops = &sdhci_cdns_ops, 240 }; 241 242 static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) 243 { 244 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); 245 void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06; 246 u32 tmp; 247 int i, ret; 248 249 if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) 250 return -EINVAL; 251 252 tmp = readl(reg); 253 tmp &= ~SDHCI_CDNS_HRS06_TUNE; 254 tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); 255 256 /* 257 * Workaround for IP errata: 258 * The IP6116 SD/eMMC PHY design has a timing issue on receive data 259 * path. Send tune request twice. 260 */ 261 for (i = 0; i < 2; i++) { 262 tmp |= SDHCI_CDNS_HRS06_TUNE_UP; 263 writel(tmp, reg); 264 265 ret = readl_poll_timeout(reg, tmp, 266 !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), 267 0, 1); 268 if (ret) 269 return ret; 270 } 271 272 return 0; 273 } 274 275 static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode) 276 { 277 struct sdhci_host *host = mmc_priv(mmc); 278 int cur_streak = 0; 279 int max_streak = 0; 280 int end_of_streak = 0; 281 int i; 282 283 /* 284 * This handler only implements the eMMC tuning that is specific to 285 * this controller. Fall back to the standard method for SD timing. 286 */ 287 if (host->timing != MMC_TIMING_MMC_HS200) 288 return sdhci_execute_tuning(mmc, opcode); 289 290 if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200)) 291 return -EINVAL; 292 293 for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) { 294 if (sdhci_cdns_set_tune_val(host, i) || 295 mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */ 296 cur_streak = 0; 297 } else { /* good */ 298 cur_streak++; 299 if (cur_streak > max_streak) { 300 max_streak = cur_streak; 301 end_of_streak = i; 302 } 303 } 304 } 305 306 if (!max_streak) { 307 dev_err(mmc_dev(host->mmc), "no tuning point found\n"); 308 return -EIO; 309 } 310 311 return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); 312 } 313 314 static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, 315 struct mmc_ios *ios) 316 { 317 struct sdhci_host *host = mmc_priv(mmc); 318 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); 319 u32 mode; 320 321 priv->enhanced_strobe = ios->enhanced_strobe; 322 323 mode = sdhci_cdns_get_emmc_mode(priv); 324 325 if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe) 326 sdhci_cdns_set_emmc_mode(priv, 327 SDHCI_CDNS_HRS06_MODE_MMC_HS400ES); 328 329 if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe) 330 sdhci_cdns_set_emmc_mode(priv, 331 SDHCI_CDNS_HRS06_MODE_MMC_HS400); 332 } 333 334 static int sdhci_cdns_probe(struct platform_device *pdev) 335 { 336 struct sdhci_host *host; 337 struct sdhci_pltfm_host *pltfm_host; 338 struct sdhci_cdns_priv *priv; 339 struct clk *clk; 340 size_t priv_size; 341 unsigned int nr_phy_params; 342 int ret; 343 struct device *dev = &pdev->dev; 344 345 clk = devm_clk_get(dev, NULL); 346 if (IS_ERR(clk)) 347 return PTR_ERR(clk); 348 349 ret = clk_prepare_enable(clk); 350 if (ret) 351 return ret; 352 353 nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node); 354 priv_size = sizeof(*priv) + sizeof(priv->phy_params[0]) * nr_phy_params; 355 host = sdhci_pltfm_init(pdev, &sdhci_cdns_pltfm_data, priv_size); 356 if (IS_ERR(host)) { 357 ret = PTR_ERR(host); 358 goto disable_clk; 359 } 360 361 pltfm_host = sdhci_priv(host); 362 pltfm_host->clk = clk; 363 364 priv = sdhci_pltfm_priv(pltfm_host); 365 priv->nr_phy_params = nr_phy_params; 366 priv->hrs_addr = host->ioaddr; 367 priv->enhanced_strobe = false; 368 host->ioaddr += SDHCI_CDNS_SRS_BASE; 369 host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning; 370 host->mmc_host_ops.hs400_enhanced_strobe = 371 sdhci_cdns_hs400_enhanced_strobe; 372 sdhci_enable_v4_mode(host); 373 374 sdhci_get_of_property(pdev); 375 376 ret = mmc_of_parse(host->mmc); 377 if (ret) 378 goto free; 379 380 sdhci_cdns_phy_param_parse(dev->of_node, priv); 381 382 ret = sdhci_cdns_phy_init(priv); 383 if (ret) 384 goto free; 385 386 ret = sdhci_add_host(host); 387 if (ret) 388 goto free; 389 390 return 0; 391 free: 392 sdhci_pltfm_free(pdev); 393 disable_clk: 394 clk_disable_unprepare(clk); 395 396 return ret; 397 } 398 399 #ifdef CONFIG_PM_SLEEP 400 static int sdhci_cdns_resume(struct device *dev) 401 { 402 struct sdhci_host *host = dev_get_drvdata(dev); 403 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 404 struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host); 405 int ret; 406 407 ret = clk_prepare_enable(pltfm_host->clk); 408 if (ret) 409 return ret; 410 411 ret = sdhci_cdns_phy_init(priv); 412 if (ret) 413 goto disable_clk; 414 415 ret = sdhci_resume_host(host); 416 if (ret) 417 goto disable_clk; 418 419 return 0; 420 421 disable_clk: 422 clk_disable_unprepare(pltfm_host->clk); 423 424 return ret; 425 } 426 #endif 427 428 static const struct dev_pm_ops sdhci_cdns_pm_ops = { 429 SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume) 430 }; 431 432 static const struct of_device_id sdhci_cdns_match[] = { 433 { .compatible = "socionext,uniphier-sd4hc" }, 434 { .compatible = "cdns,sd4hc" }, 435 { /* sentinel */ } 436 }; 437 MODULE_DEVICE_TABLE(of, sdhci_cdns_match); 438 439 static struct platform_driver sdhci_cdns_driver = { 440 .driver = { 441 .name = "sdhci-cdns", 442 .pm = &sdhci_cdns_pm_ops, 443 .of_match_table = sdhci_cdns_match, 444 }, 445 .probe = sdhci_cdns_probe, 446 .remove = sdhci_pltfm_unregister, 447 }; 448 module_platform_driver(sdhci_cdns_driver); 449 450 MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>"); 451 MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver"); 452 MODULE_LICENSE("GPL"); 453