2 * Copyright (C) 2016 Socionext Inc.
3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 * SPDX-License-Identifier: GPL-2.0+
10 #include <linux/bitfield.h>
12 #include <linux/iopoll.h>
13 #include <linux/sizes.h>
18 /* HRS - Host Register Set (specific to Cadence) */
19 #define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
20 #define SDHCI_CDNS_HRS04_ACK BIT(26)
21 #define SDHCI_CDNS_HRS04_RD BIT(25)
22 #define SDHCI_CDNS_HRS04_WR BIT(24)
23 #define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
24 #define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
25 #define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
27 #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
28 #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
29 #define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
30 #define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
31 #define SDHCI_CDNS_HRS06_MODE_SD 0x0
32 #define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
33 #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
34 #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
35 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
36 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
38 /* SRS - Slot Register Set (SDHCI-compatible) */
39 #define SDHCI_CDNS_SRS_BASE 0x200
42 #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
43 #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
44 #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
45 #define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
46 #define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
47 #define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
48 #define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
49 #define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
50 #define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
51 #define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
52 #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
53 #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
56 * The tuned val register is 6 bit-wide, but not the whole of the range is
57 * available. The range 0-42 seems to be available (then 43 wraps around to 0)
58 * but I am not quite sure if it is official. Use only 0 to 39 for safety.
60 #define SDHCI_CDNS_MAX_TUNING_LOOP 40
62 struct sdhci_cdns_plat
{
63 struct mmc_config cfg
;
65 void __iomem
*hrs_addr
;
68 struct sdhci_cdns_phy_cfg
{
73 static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs
[] = {
74 { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS
, },
75 { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT
, },
76 { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12
, },
77 { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25
, },
78 { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50
, },
79 { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50
, },
80 { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR
, },
81 { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR
, },
82 { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK
, },
83 { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC
, },
84 { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE
, },
87 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat
*plat
,
90 void __iomem
*reg
= plat
->hrs_addr
+ SDHCI_CDNS_HRS04
;
94 tmp
= FIELD_PREP(SDHCI_CDNS_HRS04_WDATA
, data
) |
95 FIELD_PREP(SDHCI_CDNS_HRS04_ADDR
, addr
);
98 tmp
|= SDHCI_CDNS_HRS04_WR
;
101 ret
= readl_poll_timeout(reg
, tmp
, tmp
& SDHCI_CDNS_HRS04_ACK
, 10);
105 tmp
&= ~SDHCI_CDNS_HRS04_WR
;
111 static int sdhci_cdns_phy_init(struct sdhci_cdns_plat
*plat
,
112 const void *fdt
, int nodeoffset
)
117 for (i
= 0; i
< ARRAY_SIZE(sdhci_cdns_phy_cfgs
); i
++) {
118 prop
= fdt_getprop(fdt
, nodeoffset
,
119 sdhci_cdns_phy_cfgs
[i
].property
, NULL
);
123 ret
= sdhci_cdns_write_phy_reg(plat
,
124 sdhci_cdns_phy_cfgs
[i
].addr
,
125 fdt32_to_cpu(*prop
));
133 static void sdhci_cdns_set_control_reg(struct sdhci_host
*host
)
135 struct mmc
*mmc
= host
->mmc
;
136 struct sdhci_cdns_plat
*plat
= dev_get_platdata(mmc
->dev
);
137 unsigned int clock
= mmc
->clock
;
142 * The mode should be decided by MMC_TIMING_* like Linux, but
143 * U-Boot does not support timing. Use the clock frequency instead.
145 if (clock
<= 26000000) {
146 mode
= SDHCI_CDNS_HRS06_MODE_SD
; /* use this for Legacy */
147 } else if (clock
<= 52000000) {
149 mode
= SDHCI_CDNS_HRS06_MODE_MMC_DDR
;
151 mode
= SDHCI_CDNS_HRS06_MODE_MMC_SDR
;
154 mode
= SDHCI_CDNS_HRS06_MODE_MMC_HS400
;
156 mode
= SDHCI_CDNS_HRS06_MODE_MMC_HS200
;
159 tmp
= readl(plat
->hrs_addr
+ SDHCI_CDNS_HRS06
);
160 tmp
&= ~SDHCI_CDNS_HRS06_MODE
;
161 tmp
|= FIELD_PREP(SDHCI_CDNS_HRS06_MODE
, mode
);
162 writel(tmp
, plat
->hrs_addr
+ SDHCI_CDNS_HRS06
);
165 static const struct sdhci_ops sdhci_cdns_ops
= {
166 .set_control_reg
= sdhci_cdns_set_control_reg
,
169 static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat
*plat
,
172 void __iomem
*reg
= plat
->hrs_addr
+ SDHCI_CDNS_HRS06
;
175 if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE
, val
)))
179 tmp
&= ~SDHCI_CDNS_HRS06_TUNE
;
180 tmp
|= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE
, val
);
181 tmp
|= SDHCI_CDNS_HRS06_TUNE_UP
;
184 return readl_poll_timeout(reg
, tmp
, !(tmp
& SDHCI_CDNS_HRS06_TUNE_UP
),
188 static int __maybe_unused
sdhci_cdns_execute_tuning(struct udevice
*dev
,
191 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
192 struct mmc
*mmc
= &plat
->mmc
;
195 int end_of_streak
= 0;
199 * This handler only implements the eMMC tuning that is specific to
200 * this controller. The tuning for SD timing should be handled by the
206 if (WARN_ON(opcode
!= MMC_CMD_SEND_TUNING_BLOCK_HS200
))
209 for (i
= 0; i
< SDHCI_CDNS_MAX_TUNING_LOOP
; i
++) {
210 if (sdhci_cdns_set_tune_val(plat
, i
) ||
211 mmc_send_tuning(mmc
, opcode
, NULL
)) { /* bad */
215 if (cur_streak
> max_streak
) {
216 max_streak
= cur_streak
;
223 dev_err(dev
, "no tuning point found\n");
227 return sdhci_cdns_set_tune_val(plat
, end_of_streak
- max_streak
/ 2);
230 static struct dm_mmc_ops sdhci_cdns_mmc_ops
;
232 static int sdhci_cdns_bind(struct udevice
*dev
)
234 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
236 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
239 static int sdhci_cdns_probe(struct udevice
*dev
)
241 DECLARE_GLOBAL_DATA_PTR
;
242 struct mmc_uclass_priv
*upriv
= dev_get_uclass_priv(dev
);
243 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
244 struct sdhci_host
*host
= dev_get_priv(dev
);
248 base
= devfdt_get_addr(dev
);
249 if (base
== FDT_ADDR_T_NONE
)
252 plat
->hrs_addr
= devm_ioremap(dev
, base
, SZ_1K
);
256 host
->name
= dev
->name
;
257 host
->ioaddr
= plat
->hrs_addr
+ SDHCI_CDNS_SRS_BASE
;
258 host
->ops
= &sdhci_cdns_ops
;
259 host
->quirks
|= SDHCI_QUIRK_WAIT_SEND_CMD
;
260 sdhci_cdns_mmc_ops
= sdhci_ops
;
261 #ifdef MMC_SUPPORTS_TUNING
262 sdhci_cdns_mmc_ops
.execute_tuning
= sdhci_cdns_execute_tuning
;
265 ret
= mmc_of_parse(dev
, &plat
->cfg
);
269 ret
= sdhci_cdns_phy_init(plat
, gd
->fdt_blob
, dev_of_offset(dev
));
273 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
277 upriv
->mmc
= &plat
->mmc
;
278 host
->mmc
= &plat
->mmc
;
279 host
->mmc
->priv
= host
;
281 return sdhci_probe(dev
);
284 static const struct udevice_id sdhci_cdns_match
[] = {
285 { .compatible
= "socionext,uniphier-sd4hc" },
286 { .compatible
= "cdns,sd4hc" },
290 U_BOOT_DRIVER(sdhci_cdns
) = {
291 .name
= "sdhci-cdns",
293 .of_match
= sdhci_cdns_match
,
294 .bind
= sdhci_cdns_bind
,
295 .probe
= sdhci_cdns_probe
,
296 .priv_auto_alloc_size
= sizeof(struct sdhci_host
),
297 .platdata_auto_alloc_size
= sizeof(struct sdhci_cdns_plat
),
298 .ops
= &sdhci_cdns_mmc_ops
,