0001
0002
0003
0004
0005
0006
0007 #include <linux/bitfield.h>
0008 #include <linux/bits.h>
0009 #include <linux/iopoll.h>
0010 #include <linux/module.h>
0011 #include <linux/mmc/host.h>
0012 #include <linux/mmc/mmc.h>
0013 #include <linux/of.h>
0014 #include <linux/of_device.h>
0015
0016 #include "sdhci-pltfm.h"
0017
0018
0019 #define SDHCI_CDNS_HRS04 0x10
0020 #define SDHCI_CDNS_HRS04_ACK BIT(26)
0021 #define SDHCI_CDNS_HRS04_RD BIT(25)
0022 #define SDHCI_CDNS_HRS04_WR BIT(24)
0023 #define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
0024 #define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
0025 #define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
0026
0027 #define SDHCI_CDNS_HRS06 0x18
0028 #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
0029 #define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
0030 #define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
0031 #define SDHCI_CDNS_HRS06_MODE_SD 0x0
0032 #define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
0033 #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
0034 #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
0035 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
0036 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
0037
0038
0039 #define SDHCI_CDNS_SRS_BASE 0x200
0040
0041
0042 #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
0043 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
0044 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
0045 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
0046 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
0047 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
0048 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
0049 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
0050 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
0051 #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
0052 #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
0053 #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
0054
0055
0056
0057
0058
0059
0060 #define SDHCI_CDNS_MAX_TUNING_LOOP 40
0061
0062 struct sdhci_cdns_phy_param {
0063 u8 addr;
0064 u8 data;
0065 };
0066
0067 struct sdhci_cdns_priv {
0068 void __iomem *hrs_addr;
0069 bool enhanced_strobe;
0070 unsigned int nr_phy_params;
0071 struct sdhci_cdns_phy_param phy_params[];
0072 };
0073
0074 struct sdhci_cdns_phy_cfg {
0075 const char *property;
0076 u8 addr;
0077 };
0078
0079 static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
0080 { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
0081 { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
0082 { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },
0083 { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, },
0084 { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, },
0085 { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, },
0086 { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, },
0087 { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, },
0088 { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, },
0089 { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, },
0090 { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
0091 };
0092
0093 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
0094 u8 addr, u8 data)
0095 {
0096 void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
0097 u32 tmp;
0098 int ret;
0099
0100 ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
0101 0, 10);
0102 if (ret)
0103 return ret;
0104
0105 tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
0106 FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
0107 writel(tmp, reg);
0108
0109 tmp |= SDHCI_CDNS_HRS04_WR;
0110 writel(tmp, reg);
0111
0112 ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
0113 if (ret)
0114 return ret;
0115
0116 tmp &= ~SDHCI_CDNS_HRS04_WR;
0117 writel(tmp, reg);
0118
0119 ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
0120 0, 10);
0121
0122 return ret;
0123 }
0124
0125 static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
0126 {
0127 unsigned int count = 0;
0128 int i;
0129
0130 for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
0131 if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
0132 count++;
0133
0134 return count;
0135 }
0136
0137 static void sdhci_cdns_phy_param_parse(struct device_node *np,
0138 struct sdhci_cdns_priv *priv)
0139 {
0140 struct sdhci_cdns_phy_param *p = priv->phy_params;
0141 u32 val;
0142 int ret, i;
0143
0144 for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
0145 ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property,
0146 &val);
0147 if (ret)
0148 continue;
0149
0150 p->addr = sdhci_cdns_phy_cfgs[i].addr;
0151 p->data = val;
0152 p++;
0153 }
0154 }
0155
0156 static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
0157 {
0158 int ret, i;
0159
0160 for (i = 0; i < priv->nr_phy_params; i++) {
0161 ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr,
0162 priv->phy_params[i].data);
0163 if (ret)
0164 return ret;
0165 }
0166
0167 return 0;
0168 }
0169
0170 static void *sdhci_cdns_priv(struct sdhci_host *host)
0171 {
0172 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
0173
0174 return sdhci_pltfm_priv(pltfm_host);
0175 }
0176
0177 static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
0178 {
0179
0180
0181
0182
0183 return host->max_clk;
0184 }
0185
0186 static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
0187 {
0188 u32 tmp;
0189
0190
0191 tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
0192 tmp &= ~SDHCI_CDNS_HRS06_MODE;
0193 tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
0194 writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
0195 }
0196
0197 static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
0198 {
0199 u32 tmp;
0200
0201 tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
0202 return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
0203 }
0204
0205 static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
0206 {
0207 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
0208 void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
0209 u32 tmp;
0210 int i, ret;
0211
0212 if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
0213 return -EINVAL;
0214
0215 tmp = readl(reg);
0216 tmp &= ~SDHCI_CDNS_HRS06_TUNE;
0217 tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
0218
0219
0220
0221
0222
0223
0224 for (i = 0; i < 2; i++) {
0225 tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
0226 writel(tmp, reg);
0227
0228 ret = readl_poll_timeout(reg, tmp,
0229 !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
0230 0, 1);
0231 if (ret)
0232 return ret;
0233 }
0234
0235 return 0;
0236 }
0237
0238
0239
0240
0241
0242 static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
0243 {
0244 int cur_streak = 0;
0245 int max_streak = 0;
0246 int end_of_streak = 0;
0247 int i;
0248
0249
0250
0251
0252
0253 if (host->timing != MMC_TIMING_MMC_HS200 &&
0254 host->timing != MMC_TIMING_UHS_SDR104)
0255 return 0;
0256
0257 for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
0258 if (sdhci_cdns_set_tune_val(host, i) ||
0259 mmc_send_tuning(host->mmc, opcode, NULL)) {
0260 cur_streak = 0;
0261 } else {
0262 cur_streak++;
0263 if (cur_streak > max_streak) {
0264 max_streak = cur_streak;
0265 end_of_streak = i;
0266 }
0267 }
0268 }
0269
0270 if (!max_streak) {
0271 dev_err(mmc_dev(host->mmc), "no tuning point found\n");
0272 return -EIO;
0273 }
0274
0275 return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
0276 }
0277
0278 static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
0279 unsigned int timing)
0280 {
0281 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
0282 u32 mode;
0283
0284 switch (timing) {
0285 case MMC_TIMING_MMC_HS:
0286 mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
0287 break;
0288 case MMC_TIMING_MMC_DDR52:
0289 mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
0290 break;
0291 case MMC_TIMING_MMC_HS200:
0292 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
0293 break;
0294 case MMC_TIMING_MMC_HS400:
0295 if (priv->enhanced_strobe)
0296 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES;
0297 else
0298 mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
0299 break;
0300 default:
0301 mode = SDHCI_CDNS_HRS06_MODE_SD;
0302 break;
0303 }
0304
0305 sdhci_cdns_set_emmc_mode(priv, mode);
0306
0307
0308 if (mode == SDHCI_CDNS_HRS06_MODE_SD)
0309 sdhci_set_uhs_signaling(host, timing);
0310 }
0311
0312 static const struct sdhci_ops sdhci_cdns_ops = {
0313 .set_clock = sdhci_set_clock,
0314 .get_timeout_clock = sdhci_cdns_get_timeout_clock,
0315 .set_bus_width = sdhci_set_bus_width,
0316 .reset = sdhci_reset,
0317 .platform_execute_tuning = sdhci_cdns_execute_tuning,
0318 .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
0319 };
0320
0321 static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
0322 .ops = &sdhci_cdns_ops,
0323 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
0324 };
0325
0326 static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
0327 .ops = &sdhci_cdns_ops,
0328 };
0329
0330 static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
0331 struct mmc_ios *ios)
0332 {
0333 struct sdhci_host *host = mmc_priv(mmc);
0334 struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
0335 u32 mode;
0336
0337 priv->enhanced_strobe = ios->enhanced_strobe;
0338
0339 mode = sdhci_cdns_get_emmc_mode(priv);
0340
0341 if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe)
0342 sdhci_cdns_set_emmc_mode(priv,
0343 SDHCI_CDNS_HRS06_MODE_MMC_HS400ES);
0344
0345 if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe)
0346 sdhci_cdns_set_emmc_mode(priv,
0347 SDHCI_CDNS_HRS06_MODE_MMC_HS400);
0348 }
0349
0350 static int sdhci_cdns_probe(struct platform_device *pdev)
0351 {
0352 struct sdhci_host *host;
0353 const struct sdhci_pltfm_data *data;
0354 struct sdhci_pltfm_host *pltfm_host;
0355 struct sdhci_cdns_priv *priv;
0356 struct clk *clk;
0357 unsigned int nr_phy_params;
0358 int ret;
0359 struct device *dev = &pdev->dev;
0360 static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
0361
0362 clk = devm_clk_get(dev, NULL);
0363 if (IS_ERR(clk))
0364 return PTR_ERR(clk);
0365
0366 ret = clk_prepare_enable(clk);
0367 if (ret)
0368 return ret;
0369
0370 data = of_device_get_match_data(dev);
0371 if (!data)
0372 data = &sdhci_cdns_pltfm_data;
0373
0374 nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
0375 host = sdhci_pltfm_init(pdev, data,
0376 struct_size(priv, phy_params, nr_phy_params));
0377 if (IS_ERR(host)) {
0378 ret = PTR_ERR(host);
0379 goto disable_clk;
0380 }
0381
0382 pltfm_host = sdhci_priv(host);
0383 pltfm_host->clk = clk;
0384
0385 priv = sdhci_pltfm_priv(pltfm_host);
0386 priv->nr_phy_params = nr_phy_params;
0387 priv->hrs_addr = host->ioaddr;
0388 priv->enhanced_strobe = false;
0389 host->ioaddr += SDHCI_CDNS_SRS_BASE;
0390 host->mmc_host_ops.hs400_enhanced_strobe =
0391 sdhci_cdns_hs400_enhanced_strobe;
0392 sdhci_enable_v4_mode(host);
0393 __sdhci_read_caps(host, &version, NULL, NULL);
0394
0395 sdhci_get_of_property(pdev);
0396
0397 ret = mmc_of_parse(host->mmc);
0398 if (ret)
0399 goto free;
0400
0401 sdhci_cdns_phy_param_parse(dev->of_node, priv);
0402
0403 ret = sdhci_cdns_phy_init(priv);
0404 if (ret)
0405 goto free;
0406
0407 ret = sdhci_add_host(host);
0408 if (ret)
0409 goto free;
0410
0411 return 0;
0412 free:
0413 sdhci_pltfm_free(pdev);
0414 disable_clk:
0415 clk_disable_unprepare(clk);
0416
0417 return ret;
0418 }
0419
0420 #ifdef CONFIG_PM_SLEEP
0421 static int sdhci_cdns_resume(struct device *dev)
0422 {
0423 struct sdhci_host *host = dev_get_drvdata(dev);
0424 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
0425 struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
0426 int ret;
0427
0428 ret = clk_prepare_enable(pltfm_host->clk);
0429 if (ret)
0430 return ret;
0431
0432 ret = sdhci_cdns_phy_init(priv);
0433 if (ret)
0434 goto disable_clk;
0435
0436 ret = sdhci_resume_host(host);
0437 if (ret)
0438 goto disable_clk;
0439
0440 return 0;
0441
0442 disable_clk:
0443 clk_disable_unprepare(pltfm_host->clk);
0444
0445 return ret;
0446 }
0447 #endif
0448
0449 static const struct dev_pm_ops sdhci_cdns_pm_ops = {
0450 SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
0451 };
0452
0453 static const struct of_device_id sdhci_cdns_match[] = {
0454 {
0455 .compatible = "socionext,uniphier-sd4hc",
0456 .data = &sdhci_cdns_uniphier_pltfm_data,
0457 },
0458 { .compatible = "cdns,sd4hc" },
0459 { }
0460 };
0461 MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
0462
0463 static struct platform_driver sdhci_cdns_driver = {
0464 .driver = {
0465 .name = "sdhci-cdns",
0466 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0467 .pm = &sdhci_cdns_pm_ops,
0468 .of_match_table = sdhci_cdns_match,
0469 },
0470 .probe = sdhci_cdns_probe,
0471 .remove = sdhci_pltfm_unregister,
0472 };
0473 module_platform_driver(sdhci_cdns_driver);
0474
0475 MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
0476 MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
0477 MODULE_LICENSE("GPL");