Prepare v2023.10
[platform/kernel/u-boot.git] / drivers / mmc / atmel_sdhci.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015 Atmel Corporation
4  *                    Wenyou.Yang <wenyou.yang@atmel.com>
5  */
6
7 #include <common.h>
8 #include <clk.h>
9 #include <dm.h>
10 #include <malloc.h>
11 #include <sdhci.h>
12 #include <asm/arch/clk.h>
13 #include <asm/global_data.h>
14
15 #define ATMEL_SDHC_MIN_FREQ     400000
16 #define ATMEL_SDHC_GCK_RATE     240000000
17
18 #define ATMEL_SDHC_MC1R 0x204
19 #define ATMEL_SDHC_MC1R_FCD     0x80
20
21 #ifndef CONFIG_DM_MMC
22 int atmel_sdhci_init(void *regbase, u32 id)
23 {
24         struct sdhci_host *host;
25         u32 max_clk, min_clk = ATMEL_SDHC_MIN_FREQ;
26
27         host = (struct sdhci_host *)calloc(1, sizeof(struct sdhci_host));
28         if (!host) {
29                 printf("%s: sdhci_host calloc failed\n", __func__);
30                 return -ENOMEM;
31         }
32
33         host->name = "atmel_sdhci";
34         host->ioaddr = regbase;
35         host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
36         max_clk = at91_get_periph_generated_clk(id);
37         if (!max_clk) {
38                 printf("%s: Failed to get the proper clock\n", __func__);
39                 free(host);
40                 return -ENODEV;
41         }
42         host->max_clk = max_clk;
43
44         add_sdhci(host, 0, min_clk);
45
46         return 0;
47 }
48
49 #else
50
51 DECLARE_GLOBAL_DATA_PTR;
52
53 struct atmel_sdhci_plat {
54         struct mmc_config cfg;
55         struct mmc mmc;
56 };
57
58 static void atmel_sdhci_config_fcd(struct sdhci_host *host)
59 {
60         u8 mc1r;
61
62         /* If nonremovable, assume that the card is always present.
63          *
64          * WA: SAMA5D2 doesn't drive CMD if using CD GPIO line.
65          */
66         if ((host->mmc->cfg->host_caps & MMC_CAP_NONREMOVABLE)
67 #if CONFIG_IS_ENABLED(DM_GPIO)
68                 || dm_gpio_get_value(&host->cd_gpio) >= 0
69 #endif
70            ) {
71                 sdhci_readb(host, ATMEL_SDHC_MC1R);
72                 mc1r |= ATMEL_SDHC_MC1R_FCD;
73                 sdhci_writeb(host, mc1r, ATMEL_SDHC_MC1R);
74         }
75 }
76
77 static int atmel_sdhci_deferred_probe(struct sdhci_host *host)
78 {
79         struct udevice *dev = host->mmc->dev;
80         int ret;
81
82         ret = sdhci_probe(dev);
83         if (ret)
84                 return ret;
85
86         atmel_sdhci_config_fcd(host);
87
88         return 0;
89 }
90
91 static const struct sdhci_ops atmel_sdhci_ops = {
92         .deferred_probe = atmel_sdhci_deferred_probe,
93 };
94
95 static int atmel_sdhci_probe(struct udevice *dev)
96 {
97         struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
98         struct atmel_sdhci_plat *plat = dev_get_plat(dev);
99         struct sdhci_host *host = dev_get_priv(dev);
100         u32 max_clk;
101         struct clk clk;
102         int ret;
103
104         ret = clk_get_by_index(dev, 0, &clk);
105         if (ret)
106                 return ret;
107
108         ret = clk_enable(&clk);
109         if (ret)
110                 return ret;
111
112         host->name = dev->name;
113         host->ioaddr = dev_read_addr_ptr(dev);
114
115         host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
116         host->bus_width = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
117                                          "bus-width", 4);
118
119         ret = clk_get_by_index(dev, 1, &clk);
120         if (ret)
121                 return ret;
122
123         clk_set_rate(&clk, ATMEL_SDHC_GCK_RATE);
124
125         max_clk = clk_get_rate(&clk);
126         if (!max_clk)
127                 return -EINVAL;
128
129         ret = clk_enable(&clk);
130         /* return error only if the clock really has a clock enable func */
131         if (ret && ret != -ENOSYS)
132                 return ret;
133
134         ret = mmc_of_parse(dev, &plat->cfg);
135         if (ret)
136                 return ret;
137
138         host->max_clk = max_clk;
139         host->mmc = &plat->mmc;
140         host->mmc->dev = dev;
141
142         ret = sdhci_setup_cfg(&plat->cfg, host, 0, ATMEL_SDHC_MIN_FREQ);
143         if (ret)
144                 return ret;
145
146         host->mmc->priv = host;
147         host->ops = &atmel_sdhci_ops;
148         upriv->mmc = host->mmc;
149
150         clk_free(&clk);
151
152         ret = sdhci_probe(dev);
153         if (ret)
154                 return ret;
155
156         atmel_sdhci_config_fcd(host);
157
158         return 0;
159 }
160
161 static int atmel_sdhci_bind(struct udevice *dev)
162 {
163         struct atmel_sdhci_plat *plat = dev_get_plat(dev);
164
165         return sdhci_bind(dev, &plat->mmc, &plat->cfg);
166 }
167
168 static const struct udevice_id atmel_sdhci_ids[] = {
169         { .compatible = "atmel,sama5d2-sdhci" },
170         { .compatible = "microchip,sam9x60-sdhci" },
171         { .compatible = "microchip,sama7g5-sdhci" },
172         { }
173 };
174
175 U_BOOT_DRIVER(atmel_sdhci_drv) = {
176         .name           = "atmel_sdhci",
177         .id             = UCLASS_MMC,
178         .of_match       = atmel_sdhci_ids,
179         .ops            = &sdhci_ops,
180         .bind           = atmel_sdhci_bind,
181         .probe          = atmel_sdhci_probe,
182         .priv_auto      = sizeof(struct sdhci_host),
183         .plat_auto      = sizeof(struct atmel_sdhci_plat),
184 };
185 #endif