xref: /openbmc/u-boot/drivers/mmc/sdhci-cadence.c (revision e30d2bd4)
1 /*
2  * Copyright (C) 2016 Socionext Inc.
3  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <linux/io.h>
10 #include <linux/sizes.h>
11 #include <dm/device.h>
12 #include <mmc.h>
13 #include <sdhci.h>
14 
15 /* HRS - Host Register Set (specific to Cadence) */
16 #define SDHCI_CDNS_HRS04		0x10		/* PHY access port */
17 #define   SDHCI_CDNS_HRS04_ACK			BIT(26)
18 #define   SDHCI_CDNS_HRS04_RD			BIT(25)
19 #define   SDHCI_CDNS_HRS04_WR			BIT(24)
20 #define   SDHCI_CDNS_HRS04_RDATA_SHIFT		12
21 #define   SDHCI_CDNS_HRS04_WDATA_SHIFT		8
22 #define   SDHCI_CDNS_HRS04_ADDR_SHIFT		0
23 
24 /* SRS - Slot Register Set (SDHCI-compatible) */
25 #define SDHCI_CDNS_SRS_BASE		0x200
26 
27 /* PHY */
28 #define SDHCI_CDNS_PHY_DLY_SD_HS	0x00
29 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT	0x01
30 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12	0x02
31 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25	0x03
32 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50	0x04
33 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50	0x05
34 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY	0x06
35 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR	0x07
36 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR	0x08
37 
38 struct sdhci_cdns_plat {
39 	struct mmc_config cfg;
40 	struct mmc mmc;
41 	void __iomem *hrs_addr;
42 };
43 
44 static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
45 				     u8 addr, u8 data)
46 {
47 	void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04;
48 	u32 tmp;
49 
50 	tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
51 	      (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
52 	writel(tmp, reg);
53 
54 	tmp |= SDHCI_CDNS_HRS04_WR;
55 	writel(tmp, reg);
56 
57 	tmp &= ~SDHCI_CDNS_HRS04_WR;
58 	writel(tmp, reg);
59 }
60 
61 static void sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat)
62 {
63 	sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_HS, 4);
64 	sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4);
65 	sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9);
66 	sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2);
67 	sdhci_cdns_write_phy_reg(plat, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3);
68 }
69 
70 static int sdhci_cdns_bind(struct udevice *dev)
71 {
72 	struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
73 
74 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
75 }
76 
77 static int sdhci_cdns_probe(struct udevice *dev)
78 {
79 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
80 	struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
81 	struct sdhci_host *host = dev_get_priv(dev);
82 	fdt_addr_t base;
83 	int ret;
84 
85 	base = dev_get_addr(dev);
86 	if (base == FDT_ADDR_T_NONE)
87 		return -EINVAL;
88 
89 	plat->hrs_addr = devm_ioremap(dev, base, SZ_1K);
90 	if (!plat->hrs_addr)
91 		return -ENOMEM;
92 
93 	host->name = dev->name;
94 	host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE;
95 	host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD;
96 
97 	sdhci_cdns_phy_init(plat);
98 
99 	ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
100 	if (ret)
101 		return ret;
102 
103 	upriv->mmc = &plat->mmc;
104 	host->mmc = &plat->mmc;
105 	host->mmc->priv = host;
106 
107 	return sdhci_probe(dev);
108 }
109 
110 static const struct udevice_id sdhci_cdns_match[] = {
111 	{ .compatible = "socionext,uniphier-sd4hc" },
112 	{ .compatible = "cdns,sd4hc" },
113 	{ /* sentinel */ }
114 };
115 
116 U_BOOT_DRIVER(sdhci_cdns) = {
117 	.name = "sdhci-cdns",
118 	.id = UCLASS_MMC,
119 	.of_match = sdhci_cdns_match,
120 	.bind = sdhci_cdns_bind,
121 	.probe = sdhci_cdns_probe,
122 	.priv_auto_alloc_size = sizeof(struct sdhci_host),
123 	.platdata_auto_alloc_size = sizeof(struct sdhci_cdns_plat),
124 	.ops = &sdhci_ops,
125 };
126