blob: 2253bbc51837051dbc70b43944f70078cf5497b9 [file] [log] [blame]
Masahiro Yamadae5e7a7c2016-12-30 22:41:46 +09001/*
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
38struct sdhci_cdns_plat {
39 struct mmc_config cfg;
40 struct mmc mmc;
41 void __iomem *hrs_addr;
42};
43
44static 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
61static 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
70static 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
77static 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
110static const struct udevice_id sdhci_cdns_match[] = {
111 { .compatible = "socionext,uniphier-sd4hc" },
112 { .compatible = "cdns,sd4hc" },
113 { /* sentinel */ }
114};
115
116U_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};