2 * Copyright (C) 2016 Socionext Inc.
3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 * SPDX-License-Identifier: GPL-2.0+
11 #include <linux/iopoll.h>
12 #include <linux/sizes.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 #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
27 #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
28 #define SDHCI_CDNS_HRS06_TUNE_SHIFT 8
29 #define SDHCI_CDNS_HRS06_TUNE_MASK 0x3f
30 #define SDHCI_CDNS_HRS06_MODE_MASK 0x7
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
55 struct sdhci_cdns_plat
{
56 struct mmc_config cfg
;
58 void __iomem
*hrs_addr
;
61 struct sdhci_cdns_phy_cfg
{
66 static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs
[] = {
67 { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS
, },
68 { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT
, },
69 { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12
, },
70 { "cdns,phy-input-delay-sd-uhs-sdr25", SDHCI_CDNS_PHY_DLY_UHS_SDR25
, },
71 { "cdns,phy-input-delay-sd-uhs-sdr50", SDHCI_CDNS_PHY_DLY_UHS_SDR50
, },
72 { "cdns,phy-input-delay-sd-uhs-ddr50", SDHCI_CDNS_PHY_DLY_UHS_DDR50
, },
73 { "cdns,phy-input-delay-mmc-highspeed", SDHCI_CDNS_PHY_DLY_EMMC_SDR
, },
74 { "cdns,phy-input-delay-mmc-ddr", SDHCI_CDNS_PHY_DLY_EMMC_DDR
, },
75 { "cdns,phy-dll-delay-sdclk", SDHCI_CDNS_PHY_DLY_SDCLK
, },
76 { "cdns,phy-dll-delay-sdclk-hsmmc", SDHCI_CDNS_PHY_DLY_HSMMC
, },
77 { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE
, },
80 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat
*plat
,
83 void __iomem
*reg
= plat
->hrs_addr
+ SDHCI_CDNS_HRS04
;
87 tmp
= (data
<< SDHCI_CDNS_HRS04_WDATA_SHIFT
) |
88 (addr
<< SDHCI_CDNS_HRS04_ADDR_SHIFT
);
91 tmp
|= SDHCI_CDNS_HRS04_WR
;
94 ret
= readl_poll_timeout(reg
, tmp
, tmp
& SDHCI_CDNS_HRS04_ACK
, 10);
98 tmp
&= ~SDHCI_CDNS_HRS04_WR
;
104 static int sdhci_cdns_phy_init(struct sdhci_cdns_plat
*plat
,
105 const void *fdt
, int nodeoffset
)
110 for (i
= 0; i
< ARRAY_SIZE(sdhci_cdns_phy_cfgs
); i
++) {
111 prop
= fdt_getprop(fdt
, nodeoffset
,
112 sdhci_cdns_phy_cfgs
[i
].property
, NULL
);
116 ret
= sdhci_cdns_write_phy_reg(plat
,
117 sdhci_cdns_phy_cfgs
[i
].addr
,
118 fdt32_to_cpu(*prop
));
126 static void sdhci_cdns_set_control_reg(struct sdhci_host
*host
)
128 struct mmc
*mmc
= host
->mmc
;
129 struct sdhci_cdns_plat
*plat
= dev_get_platdata(mmc
->dev
);
130 unsigned int clock
= mmc
->clock
;
135 * The mode should be decided by MMC_TIMING_* like Linux, but
136 * U-Boot does not support timing. Use the clock frequency instead.
138 if (clock
<= 26000000)
139 mode
= SDHCI_CDNS_HRS06_MODE_SD
; /* use this for Legacy */
140 else if (clock
<= 52000000) {
142 mode
= SDHCI_CDNS_HRS06_MODE_MMC_DDR
;
144 mode
= SDHCI_CDNS_HRS06_MODE_MMC_SDR
;
148 * The IP supports HS200/HS400, revisit once U-Boot support it
150 printf("unsupported frequency %d\n", clock
);
154 tmp
= readl(plat
->hrs_addr
+ SDHCI_CDNS_HRS06
);
155 tmp
&= ~SDHCI_CDNS_HRS06_MODE_MASK
;
157 writel(tmp
, plat
->hrs_addr
+ SDHCI_CDNS_HRS06
);
160 static const struct sdhci_ops sdhci_cdns_ops
= {
161 .set_control_reg
= sdhci_cdns_set_control_reg
,
164 static int sdhci_cdns_bind(struct udevice
*dev
)
166 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
168 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
171 static int sdhci_cdns_probe(struct udevice
*dev
)
173 DECLARE_GLOBAL_DATA_PTR
;
174 struct mmc_uclass_priv
*upriv
= dev_get_uclass_priv(dev
);
175 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
176 struct sdhci_host
*host
= dev_get_priv(dev
);
180 base
= devfdt_get_addr(dev
);
181 if (base
== FDT_ADDR_T_NONE
)
184 plat
->hrs_addr
= devm_ioremap(dev
, base
, SZ_1K
);
188 host
->name
= dev
->name
;
189 host
->ioaddr
= plat
->hrs_addr
+ SDHCI_CDNS_SRS_BASE
;
190 host
->ops
= &sdhci_cdns_ops
;
191 host
->quirks
|= SDHCI_QUIRK_WAIT_SEND_CMD
;
193 ret
= sdhci_cdns_phy_init(plat
, gd
->fdt_blob
, dev_of_offset(dev
));
197 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
201 upriv
->mmc
= &plat
->mmc
;
202 host
->mmc
= &plat
->mmc
;
203 host
->mmc
->priv
= host
;
205 return sdhci_probe(dev
);
208 static const struct udevice_id sdhci_cdns_match
[] = {
209 { .compatible
= "socionext,uniphier-sd4hc" },
210 { .compatible
= "cdns,sd4hc" },
214 U_BOOT_DRIVER(sdhci_cdns
) = {
215 .name
= "sdhci-cdns",
217 .of_match
= sdhci_cdns_match
,
218 .bind
= sdhci_cdns_bind
,
219 .probe
= sdhci_cdns_probe
,
220 .priv_auto_alloc_size
= sizeof(struct sdhci_host
),
221 .platdata_auto_alloc_size
= sizeof(struct sdhci_cdns_plat
),