2 * Copyright (C) 2016 Socionext Inc.
3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5 * SPDX-License-Identifier: GPL-2.0+
10 #include <linux/sizes.h>
11 #include <dm/device.h>
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
24 /* SRS - Slot Register Set (SDHCI-compatible) */
25 #define SDHCI_CDNS_SRS_BASE 0x200
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
38 struct sdhci_cdns_plat
{
39 struct mmc_config cfg
;
41 void __iomem
*hrs_addr
;
44 static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat
*plat
,
47 void __iomem
*reg
= plat
->hrs_addr
+ SDHCI_CDNS_HRS04
;
50 tmp
= (data
<< SDHCI_CDNS_HRS04_WDATA_SHIFT
) |
51 (addr
<< SDHCI_CDNS_HRS04_ADDR_SHIFT
);
54 tmp
|= SDHCI_CDNS_HRS04_WR
;
57 tmp
&= ~SDHCI_CDNS_HRS04_WR
;
61 static void sdhci_cdns_phy_init(struct sdhci_cdns_plat
*plat
)
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);
70 static int sdhci_cdns_bind(struct udevice
*dev
)
72 struct sdhci_cdns_plat
*plat
= dev_get_platdata(dev
);
74 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
77 static int sdhci_cdns_probe(struct udevice
*dev
)
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
);
85 base
= dev_get_addr(dev
);
86 if (base
== FDT_ADDR_T_NONE
)
89 plat
->hrs_addr
= devm_ioremap(dev
, base
, SZ_1K
);
93 host
->name
= dev
->name
;
94 host
->ioaddr
= plat
->hrs_addr
+ SDHCI_CDNS_SRS_BASE
;
95 host
->quirks
|= SDHCI_QUIRK_WAIT_SEND_CMD
;
97 sdhci_cdns_phy_init(plat
);
99 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
103 upriv
->mmc
= &plat
->mmc
;
104 host
->mmc
= &plat
->mmc
;
105 host
->mmc
->priv
= host
;
107 return sdhci_probe(dev
);
110 static const struct udevice_id sdhci_cdns_match
[] = {
111 { .compatible
= "socionext,uniphier-sd4hc" },
112 { .compatible
= "cdns,sd4hc" },
116 U_BOOT_DRIVER(sdhci_cdns
) = {
117 .name
= "sdhci-cdns",
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
),