]> git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/mmc/sdhci-cadence.c
Merge tag 'xilinx-for-v2017.03' of git://www.denx.de/git/u-boot-microblaze
[people/ms/u-boot.git] / drivers / mmc / sdhci-cadence.c
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 };