xref: /rk3399_rockchip-uboot/drivers/mmc/sdhci-cadence.c (revision 0e00a84cdedf7a1949486746225b35984b351eca)
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