Merge branch 'master' of git://git.denx.de/u-boot-video
[oweals/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 };