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