1
2
3
4
5
6
7#include <common.h>
8#include <dm.h>
9#include <log.h>
10#include <malloc.h>
11#include <sdhci.h>
12#include <fdtdec.h>
13#include <linux/libfdt.h>
14#include <asm/gpio.h>
15#include <asm/arch/mmc.h>
16#include <asm/arch/clk.h>
17#include <errno.h>
18#include <asm/arch/pinmux.h>
19
20#ifdef CONFIG_DM_MMC
21struct s5p_sdhci_plat {
22 struct mmc_config cfg;
23 struct mmc mmc;
24};
25
26DECLARE_GLOBAL_DATA_PTR;
27#endif
28
29static char *S5P_NAME = "SAMSUNG SDHCI";
30static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
31{
32 unsigned long val, ctrl;
33
34
35
36
37
38
39
40 sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4);
41
42 val = sdhci_readl(host, SDHCI_CONTROL2);
43 val &= SDHCI_CTRL2_SELBASECLK_MASK(3);
44
45 val |= SDHCI_CTRL2_ENSTAASYNCCLR |
46 SDHCI_CTRL2_ENCMDCNFMSK |
47 SDHCI_CTRL2_ENFBCLKRX |
48 SDHCI_CTRL2_ENCLKOUTHOLD;
49
50 sdhci_writel(host, val, SDHCI_CONTROL2);
51
52
53
54
55
56
57
58
59
60
61 val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1;
62 sdhci_writel(host, val, SDHCI_CONTROL3);
63
64
65
66
67
68
69
70 ctrl = sdhci_readl(host, SDHCI_CONTROL2);
71 ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3);
72 ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2);
73 sdhci_writel(host, ctrl, SDHCI_CONTROL2);
74}
75
76static void s5p_set_clock(struct sdhci_host *host, u32 div)
77{
78
79 set_mmc_clk(host->index, div);
80}
81
82static const struct sdhci_ops s5p_sdhci_ops = {
83 .set_clock = &s5p_set_clock,
84 .set_control_reg = &s5p_sdhci_set_control_reg,
85};
86
87static int s5p_sdhci_core_init(struct sdhci_host *host)
88{
89 host->name = S5P_NAME;
90
91 host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
92 SDHCI_QUIRK_32BIT_DMA_ADDR |
93 SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;
94 host->max_clk = 52000000;
95 host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
96 host->ops = &s5p_sdhci_ops;
97
98 if (host->bus_width == 8)
99 host->host_caps |= MMC_MODE_8BIT;
100
101#ifndef CONFIG_BLK
102 return add_sdhci(host, 0, 400000);
103#else
104 return 0;
105#endif
106}
107
108int s5p_sdhci_init(u32 regbase, int index, int bus_width)
109{
110 struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host));
111 if (!host) {
112 printf("sdhci__host allocation fail!\n");
113 return -ENOMEM;
114 }
115 host->ioaddr = (void *)regbase;
116 host->index = index;
117 host->bus_width = bus_width;
118
119 return s5p_sdhci_core_init(host);
120}
121
122static int do_sdhci_init(struct sdhci_host *host)
123{
124 int dev_id, flag, ret;
125
126 flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
127 dev_id = host->index + PERIPH_ID_SDMMC0;
128
129 ret = exynos_pinmux_config(dev_id, flag);
130 if (ret) {
131 printf("external SD not configured\n");
132 return ret;
133 }
134
135 if (dm_gpio_is_valid(&host->pwr_gpio)) {
136 dm_gpio_set_value(&host->pwr_gpio, 1);
137 ret = exynos_pinmux_config(dev_id, flag);
138 if (ret) {
139 debug("MMC not configured\n");
140 return ret;
141 }
142 }
143
144 if (dm_gpio_is_valid(&host->cd_gpio)) {
145 ret = dm_gpio_get_value(&host->cd_gpio);
146 if (ret) {
147 debug("no SD card detected (%d)\n", ret);
148 return -ENODEV;
149 }
150 }
151
152 return s5p_sdhci_core_init(host);
153}
154
155static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
156{
157 int bus_width, dev_id;
158 unsigned int base;
159
160
161 dev_id = pinmux_decode_periph_id(blob, node);
162 if (dev_id < PERIPH_ID_SDMMC0 || dev_id > PERIPH_ID_SDMMC3) {
163 debug("MMC: Can't get device id\n");
164 return -EINVAL;
165 }
166 host->index = dev_id - PERIPH_ID_SDMMC0;
167
168
169 bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
170 if (bus_width <= 0) {
171 debug("MMC: Can't get bus-width\n");
172 return -EINVAL;
173 }
174 host->bus_width = bus_width;
175
176
177 base = fdtdec_get_addr(blob, node, "reg");
178 if (!base) {
179 debug("MMC: Can't get base address\n");
180 return -EINVAL;
181 }
182 host->ioaddr = (void *)base;
183
184 gpio_request_by_name_nodev(offset_to_ofnode(node), "pwr-gpios", 0,
185 &host->pwr_gpio, GPIOD_IS_OUT);
186 gpio_request_by_name_nodev(offset_to_ofnode(node), "cd-gpios", 0,
187 &host->cd_gpio, GPIOD_IS_IN);
188
189 return 0;
190}
191
192#ifdef CONFIG_DM_MMC
193static int s5p_sdhci_probe(struct udevice *dev)
194{
195 struct s5p_sdhci_plat *plat = dev_get_platdata(dev);
196 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
197 struct sdhci_host *host = dev_get_priv(dev);
198 int ret;
199
200 ret = sdhci_get_config(gd->fdt_blob, dev_of_offset(dev), host);
201 if (ret)
202 return ret;
203
204 ret = do_sdhci_init(host);
205 if (ret)
206 return ret;
207
208 ret = mmc_of_parse(dev, &plat->cfg);
209 if (ret)
210 return ret;
211
212 host->mmc = &plat->mmc;
213 host->mmc->dev = dev;
214
215 ret = sdhci_setup_cfg(&plat->cfg, host, 0, 400000);
216 if (ret)
217 return ret;
218
219 host->mmc->priv = host;
220 upriv->mmc = host->mmc;
221
222 return sdhci_probe(dev);
223}
224
225static int s5p_sdhci_bind(struct udevice *dev)
226{
227 struct s5p_sdhci_plat *plat = dev_get_platdata(dev);
228 int ret;
229
230 ret = sdhci_bind(dev, &plat->mmc, &plat->cfg);
231 if (ret)
232 return ret;
233
234 return 0;
235}
236
237static const struct udevice_id s5p_sdhci_ids[] = {
238 { .compatible = "samsung,exynos4412-sdhci"},
239 { }
240};
241
242U_BOOT_DRIVER(s5p_sdhci_drv) = {
243 .name = "s5p_sdhci",
244 .id = UCLASS_MMC,
245 .of_match = s5p_sdhci_ids,
246 .bind = s5p_sdhci_bind,
247 .ops = &sdhci_ops,
248 .probe = s5p_sdhci_probe,
249 .priv_auto_alloc_size = sizeof(struct sdhci_host),
250 .platdata_auto_alloc_size = sizeof(struct s5p_sdhci_plat),
251};
252#endif
253