1e5e7a7c2SMasahiro Yamada /*
2e5e7a7c2SMasahiro Yamada * Copyright (C) 2016 Socionext Inc.
3e5e7a7c2SMasahiro Yamada * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4e5e7a7c2SMasahiro Yamada *
5e5e7a7c2SMasahiro Yamada * SPDX-License-Identifier: GPL-2.0+
6e5e7a7c2SMasahiro Yamada */
7e5e7a7c2SMasahiro Yamada
8e5e7a7c2SMasahiro Yamada #include <common.h>
99d922450SSimon Glass #include <dm.h>
10e5e7a7c2SMasahiro Yamada #include <linux/io.h>
110cacd6b7SMasahiro Yamada #include <linux/iopoll.h>
12e5e7a7c2SMasahiro Yamada #include <linux/sizes.h>
13*0e00a84cSMasahiro Yamada #include <linux/libfdt.h>
14e5e7a7c2SMasahiro Yamada #include <mmc.h>
15e5e7a7c2SMasahiro Yamada #include <sdhci.h>
16e5e7a7c2SMasahiro Yamada
17e5e7a7c2SMasahiro Yamada /* HRS - Host Register Set (specific to Cadence) */
18e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
19e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_ACK BIT(26)
20e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_RD BIT(25)
21e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_WR BIT(24)
220cacd6b7SMasahiro Yamada #define SDHCI_CDNS_HRS04_RDATA_SHIFT 16
23e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
24e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
25e5e7a7c2SMasahiro Yamada
26e5e7a7c2SMasahiro Yamada /* SRS - Slot Register Set (SDHCI-compatible) */
27e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_SRS_BASE 0x200
28e5e7a7c2SMasahiro Yamada
29e5e7a7c2SMasahiro Yamada /* PHY */
30e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
31e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
32e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
33e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
34e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
35e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
36e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
37e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
38e5e7a7c2SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
390cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
400cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
410cacd6b7SMasahiro Yamada #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
42e5e7a7c2SMasahiro Yamada
43e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat {
44e5e7a7c2SMasahiro Yamada struct mmc_config cfg;
45e5e7a7c2SMasahiro Yamada struct mmc mmc;
46e5e7a7c2SMasahiro Yamada void __iomem *hrs_addr;
47e5e7a7c2SMasahiro Yamada };
48e5e7a7c2SMasahiro Yamada
490cacd6b7SMasahiro Yamada struct sdhci_cdns_phy_cfg {
500cacd6b7SMasahiro Yamada const char *property;
510cacd6b7SMasahiro Yamada u8 addr;
520cacd6b7SMasahiro Yamada };
530cacd6b7SMasahiro Yamada
540cacd6b7SMasahiro Yamada static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
550cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
560cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
570cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, },
580cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25, },
590cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50, },
600cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50, },
610cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR, },
620cacd6b7SMasahiro Yamada { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR, },
630cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK, },
640cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC, },
650cacd6b7SMasahiro Yamada { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
660cacd6b7SMasahiro Yamada };
670cacd6b7SMasahiro Yamada
sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat * plat,u8 addr,u8 data)680cacd6b7SMasahiro Yamada static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
69e5e7a7c2SMasahiro Yamada u8 addr, u8 data)
70e5e7a7c2SMasahiro Yamada {
71e5e7a7c2SMasahiro Yamada void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04;
72e5e7a7c2SMasahiro Yamada u32 tmp;
730cacd6b7SMasahiro Yamada int ret;
74e5e7a7c2SMasahiro Yamada
75e5e7a7c2SMasahiro Yamada tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
76e5e7a7c2SMasahiro Yamada (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
77e5e7a7c2SMasahiro Yamada writel(tmp, reg);
78e5e7a7c2SMasahiro Yamada
79e5e7a7c2SMasahiro Yamada tmp |= SDHCI_CDNS_HRS04_WR;
80e5e7a7c2SMasahiro Yamada writel(tmp, reg);
81e5e7a7c2SMasahiro Yamada
820cacd6b7SMasahiro Yamada ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10);
830cacd6b7SMasahiro Yamada if (ret)
840cacd6b7SMasahiro Yamada return ret;
850cacd6b7SMasahiro Yamada
86e5e7a7c2SMasahiro Yamada tmp &= ~SDHCI_CDNS_HRS04_WR;
87e5e7a7c2SMasahiro Yamada writel(tmp, reg);
880cacd6b7SMasahiro Yamada
890cacd6b7SMasahiro Yamada return 0;
90e5e7a7c2SMasahiro Yamada }
91e5e7a7c2SMasahiro Yamada
sdhci_cdns_phy_init(struct sdhci_cdns_plat * plat,const void * fdt,int nodeoffset)920cacd6b7SMasahiro Yamada static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
930cacd6b7SMasahiro Yamada const void *fdt, int nodeoffset)
94e5e7a7c2SMasahiro Yamada {
95aae6f016SMasahiro Yamada const fdt32_t *prop;
960cacd6b7SMasahiro Yamada int ret, i;
970cacd6b7SMasahiro Yamada
980cacd6b7SMasahiro Yamada for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
990cacd6b7SMasahiro Yamada prop = fdt_getprop(fdt, nodeoffset,
1000cacd6b7SMasahiro Yamada sdhci_cdns_phy_cfgs[i].property, NULL);
1010cacd6b7SMasahiro Yamada if (!prop)
1020cacd6b7SMasahiro Yamada continue;
1030cacd6b7SMasahiro Yamada
1040cacd6b7SMasahiro Yamada ret = sdhci_cdns_write_phy_reg(plat,
1050cacd6b7SMasahiro Yamada sdhci_cdns_phy_cfgs[i].addr,
1060cacd6b7SMasahiro Yamada fdt32_to_cpu(*prop));
1070cacd6b7SMasahiro Yamada if (ret)
1080cacd6b7SMasahiro Yamada return ret;
1090cacd6b7SMasahiro Yamada }
1100cacd6b7SMasahiro Yamada
1110cacd6b7SMasahiro Yamada return 0;
112e5e7a7c2SMasahiro Yamada }
113e5e7a7c2SMasahiro Yamada
sdhci_cdns_bind(struct udevice * dev)114e5e7a7c2SMasahiro Yamada static int sdhci_cdns_bind(struct udevice *dev)
115e5e7a7c2SMasahiro Yamada {
116e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
117e5e7a7c2SMasahiro Yamada
118e5e7a7c2SMasahiro Yamada return sdhci_bind(dev, &plat->mmc, &plat->cfg);
119e5e7a7c2SMasahiro Yamada }
120e5e7a7c2SMasahiro Yamada
sdhci_cdns_probe(struct udevice * dev)121e5e7a7c2SMasahiro Yamada static int sdhci_cdns_probe(struct udevice *dev)
122e5e7a7c2SMasahiro Yamada {
1230cacd6b7SMasahiro Yamada DECLARE_GLOBAL_DATA_PTR;
124e5e7a7c2SMasahiro Yamada struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
125e5e7a7c2SMasahiro Yamada struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
126e5e7a7c2SMasahiro Yamada struct sdhci_host *host = dev_get_priv(dev);
127e5e7a7c2SMasahiro Yamada fdt_addr_t base;
128e5e7a7c2SMasahiro Yamada int ret;
129e5e7a7c2SMasahiro Yamada
130a821c4afSSimon Glass base = devfdt_get_addr(dev);
131e5e7a7c2SMasahiro Yamada if (base == FDT_ADDR_T_NONE)
132e5e7a7c2SMasahiro Yamada return -EINVAL;
133e5e7a7c2SMasahiro Yamada
134e5e7a7c2SMasahiro Yamada plat->hrs_addr = devm_ioremap(dev, base, SZ_1K);
135e5e7a7c2SMasahiro Yamada if (!plat->hrs_addr)
136e5e7a7c2SMasahiro Yamada return -ENOMEM;
137e5e7a7c2SMasahiro Yamada
138e5e7a7c2SMasahiro Yamada host->name = dev->name;
139e5e7a7c2SMasahiro Yamada host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE;
140e5e7a7c2SMasahiro Yamada host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD;
141e5e7a7c2SMasahiro Yamada
142da409cccSSimon Glass ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
1430cacd6b7SMasahiro Yamada if (ret)
1440cacd6b7SMasahiro Yamada return ret;
145e5e7a7c2SMasahiro Yamada
146e5e7a7c2SMasahiro Yamada ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
147e5e7a7c2SMasahiro Yamada if (ret)
148e5e7a7c2SMasahiro Yamada return ret;
149e5e7a7c2SMasahiro Yamada
150e5e7a7c2SMasahiro Yamada upriv->mmc = &plat->mmc;
151e5e7a7c2SMasahiro Yamada host->mmc = &plat->mmc;
152e5e7a7c2SMasahiro Yamada host->mmc->priv = host;
153e5e7a7c2SMasahiro Yamada
154e5e7a7c2SMasahiro Yamada return sdhci_probe(dev);
155e5e7a7c2SMasahiro Yamada }
156e5e7a7c2SMasahiro Yamada
157e5e7a7c2SMasahiro Yamada static const struct udevice_id sdhci_cdns_match[] = {
158e5e7a7c2SMasahiro Yamada { .compatible = "socionext,uniphier-sd4hc" },
159e5e7a7c2SMasahiro Yamada { .compatible = "cdns,sd4hc" },
160e5e7a7c2SMasahiro Yamada { /* sentinel */ }
161e5e7a7c2SMasahiro Yamada };
162e5e7a7c2SMasahiro Yamada
163e5e7a7c2SMasahiro Yamada U_BOOT_DRIVER(sdhci_cdns) = {
164e5e7a7c2SMasahiro Yamada .name = "sdhci-cdns",
165e5e7a7c2SMasahiro Yamada .id = UCLASS_MMC,
166e5e7a7c2SMasahiro Yamada .of_match = sdhci_cdns_match,
167e5e7a7c2SMasahiro Yamada .bind = sdhci_cdns_bind,
168e5e7a7c2SMasahiro Yamada .probe = sdhci_cdns_probe,
169e5e7a7c2SMasahiro Yamada .priv_auto_alloc_size = sizeof(struct sdhci_host),
170e5e7a7c2SMasahiro Yamada .platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat),
171e5e7a7c2SMasahiro Yamada .ops = &sdhci_ops,
172e5e7a7c2SMasahiro Yamada };
173