2 * Copyright (C) 2016 Socionext Inc.
3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 * SPDX-License-Identifier: GPL-2.0+
10 #include <linux/iopoll.h>
11 #include <linux/sizes.h>
12 #include <dm/device.h>
17 /* HRS - Host Register Set (specific to Cadence) */
18 #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
19 #define SDHCI_CDNS_HRS04_ACK BIT(26)
20 #define SDHCI_CDNS_HRS04_RD BIT(25)
21 #define SDHCI_CDNS_HRS04_WR BIT(24)
22 #define SDHCI_CDNS_HRS04_RDATA_SHIFT 16
23 #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
24 #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
26 /* SRS - Slot Register Set (SDHCI-compatible) */
27 #define SDHCI_CDNS_SRS_BASE 0x200
30 #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
31 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
32 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
33 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
34 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
35 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
36 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
37 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
38 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
39 #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
40 #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
41 #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
43 struct sdhci_cdns_plat
{
44 struct mmc_config cfg
;
46 void __iomem
*hrs_addr
;
49 struct sdhci_cdns_phy_cfg
{
54 static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs
[] = {
55 { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS
, },
56 { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT
, },
57 { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12
, },
58 { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25
, },
59 { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50
, },
60 { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50
, },
61 { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR
, },
62 { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR
, },
63 { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK
, },
64 { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC
, },
65 { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE
, },
68 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat
*plat
,
71 void __iomem
*reg
= plat
->hrs_addr
+ SDHCI_CDNS_HRS04
;
75 tmp
= (data
<< SDHCI_CDNS_HRS04_WDATA_SHIFT
) |
76 (addr
<< SDHCI_CDNS_HRS04_ADDR_SHIFT
);
79 tmp
|= SDHCI_CDNS_HRS04_WR
;
82 ret
= readl_poll_timeout(reg
, tmp
, tmp
& SDHCI_CDNS_HRS04_ACK
, 10);
86 tmp
&= ~SDHCI_CDNS_HRS04_WR
;
92 static int sdhci_cdns_phy_init(struct sdhci_cdns_plat
*plat
,
93 const void *fdt
, int nodeoffset
)
98 for (i
= 0; i
< ARRAY_SIZE(sdhci_cdns_phy_cfgs
); i
++) {
99 prop
= fdt_getprop(fdt
, nodeoffset
,
100 sdhci_cdns_phy_cfgs
[i
].property
, NULL
);
104 ret
= sdhci_cdns_write_phy_reg(plat
,
105 sdhci_cdns_phy_cfgs
[i
].addr
,
106 fdt32_to_cpu(*prop
));
114 static int sdhci_cdns_bind(struct udevice
*dev
)
116 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
118 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
121 static int sdhci_cdns_probe(struct udevice
*dev
)
123 DECLARE_GLOBAL_DATA_PTR
;
124 struct mmc_uclass_priv
*upriv
= dev_get_uclass_priv(dev
);
125 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
126 struct sdhci_host
*host
= dev_get_priv(dev
);
130 base
= dev_get_addr(dev
);
131 if (base
== FDT_ADDR_T_NONE
)
134 plat
->hrs_addr
= devm_ioremap(dev
, base
, SZ_1K
);
138 host
->name
= dev
->name
;
139 host
->ioaddr
= plat
->hrs_addr
+ SDHCI_CDNS_SRS_BASE
;
140 host
->quirks
|= SDHCI_QUIRK_WAIT_SEND_CMD
;
142 ret
= sdhci_cdns_phy_init(plat
, gd
->fdt_blob
, dev
->of_offset
);
146 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
150 upriv
->mmc
= &plat
->mmc
;
151 host
->mmc
= &plat
->mmc
;
152 host
->mmc
->priv
= host
;
154 return sdhci_probe(dev
);
157 static const struct udevice_id sdhci_cdns_match
[] = {
158 { .compatible
= "socionext,uniphier-sd4hc" },
159 { .compatible
= "cdns,sd4hc" },
163 U_BOOT_DRIVER(sdhci_cdns
) = {
164 .name
= "sdhci-cdns",
166 .of_match
= sdhci_cdns_match
,
167 .bind
= sdhci_cdns_bind
,
168 .probe
= sdhci_cdns_probe
,
169 .priv_auto_alloc_size
= sizeof(struct sdhci_host
),
170 .platdata_auto_alloc_size
= sizeof(struct sdhci_cdns_plat
),