]>
Commit | Line | Data |
---|---|---|
442d5568 JC |
1 | /* |
2 | * (C) Copyright 2012 SAMSUNG Electronics | |
3 | * Jaehoon Chung <jh80.chung@samsung.com> | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
442d5568 JC |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <malloc.h> | |
10 | #include <sdhci.h> | |
3577fe8b PW |
11 | #include <fdtdec.h> |
12 | #include <libfdt.h> | |
13 | #include <asm/gpio.h> | |
442d5568 | 14 | #include <asm/arch/mmc.h> |
b09ed6e4 | 15 | #include <asm/arch/clk.h> |
3577fe8b | 16 | #include <errno.h> |
3577fe8b | 17 | #include <asm/arch/pinmux.h> |
442d5568 JC |
18 | |
19 | static char *S5P_NAME = "SAMSUNG SDHCI"; | |
20 | static void s5p_sdhci_set_control_reg(struct sdhci_host *host) | |
21 | { | |
22 | unsigned long val, ctrl; | |
23 | /* | |
24 | * SELCLKPADDS[17:16] | |
25 | * 00 = 2mA | |
26 | * 01 = 4mA | |
27 | * 10 = 7mA | |
28 | * 11 = 9mA | |
29 | */ | |
30 | sdhci_writel(host, SDHCI_CTRL4_DRIVE_MASK(0x3), SDHCI_CONTROL4); | |
31 | ||
32 | val = sdhci_readl(host, SDHCI_CONTROL2); | |
8ebde4f0 | 33 | val &= SDHCI_CTRL2_SELBASECLK_MASK(3); |
442d5568 JC |
34 | |
35 | val |= SDHCI_CTRL2_ENSTAASYNCCLR | | |
36 | SDHCI_CTRL2_ENCMDCNFMSK | | |
37 | SDHCI_CTRL2_ENFBCLKRX | | |
38 | SDHCI_CTRL2_ENCLKOUTHOLD; | |
39 | ||
40 | sdhci_writel(host, val, SDHCI_CONTROL2); | |
41 | ||
42 | /* | |
43 | * FCSEL3[31] FCSEL2[23] FCSEL1[15] FCSEL0[7] | |
44 | * FCSel[1:0] : Rx Feedback Clock Delay Control | |
45 | * Inverter delay means10ns delay if SDCLK 50MHz setting | |
46 | * 01 = Delay1 (basic delay) | |
47 | * 11 = Delay2 (basic delay + 2ns) | |
48 | * 00 = Delay3 (inverter delay) | |
49 | * 10 = Delay4 (inverter delay + 2ns) | |
50 | */ | |
b268660c | 51 | val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1; |
442d5568 JC |
52 | sdhci_writel(host, val, SDHCI_CONTROL3); |
53 | ||
54 | /* | |
55 | * SELBASECLK[5:4] | |
56 | * 00/01 = HCLK | |
57 | * 10 = EPLL | |
58 | * 11 = XTI or XEXTCLK | |
59 | */ | |
60 | ctrl = sdhci_readl(host, SDHCI_CONTROL2); | |
61 | ctrl &= ~SDHCI_CTRL2_SELBASECLK_MASK(0x3); | |
62 | ctrl |= SDHCI_CTRL2_SELBASECLK_MASK(0x2); | |
63 | sdhci_writel(host, ctrl, SDHCI_CONTROL2); | |
64 | } | |
65 | ||
9b8c9a3c | 66 | static int s5p_sdhci_core_init(struct sdhci_host *host) |
442d5568 | 67 | { |
442d5568 | 68 | host->name = S5P_NAME; |
442d5568 | 69 | |
b268660c | 70 | host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | |
a034ec06 | 71 | SDHCI_QUIRK_32BIT_DMA_ADDR | |
113e5dfc | 72 | SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; |
442d5568 | 73 | host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; |
b268660c | 74 | host->version = sdhci_readw(host, SDHCI_HOST_VERSION); |
442d5568 JC |
75 | |
76 | host->set_control_reg = &s5p_sdhci_set_control_reg; | |
b09ed6e4 | 77 | host->set_clock = set_mmc_clk; |
442d5568 | 78 | |
9b8c9a3c | 79 | if (host->bus_width == 8) |
113e5dfc | 80 | host->host_caps |= MMC_MODE_8BIT; |
442d5568 | 81 | |
a68aac49 | 82 | return add_sdhci(host, 52000000, 400000); |
442d5568 | 83 | } |
3577fe8b | 84 | |
9b8c9a3c JC |
85 | int s5p_sdhci_init(u32 regbase, int index, int bus_width) |
86 | { | |
1a9d1731 | 87 | struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host)); |
9b8c9a3c | 88 | if (!host) { |
1a9d1731 | 89 | printf("sdhci__host allocation fail!\n"); |
9b8c9a3c JC |
90 | return 1; |
91 | } | |
92 | host->ioaddr = (void *)regbase; | |
93 | host->index = index; | |
94 | host->bus_width = bus_width; | |
95 | ||
96 | return s5p_sdhci_core_init(host); | |
97 | } | |
98 | ||
0f925822 | 99 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
3577fe8b PW |
100 | struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; |
101 | ||
102 | static int do_sdhci_init(struct sdhci_host *host) | |
103 | { | |
2308ea7c | 104 | int dev_id, flag, ret; |
3577fe8b PW |
105 | |
106 | flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; | |
107 | dev_id = host->index + PERIPH_ID_SDMMC0; | |
108 | ||
96094d4c PM |
109 | ret = exynos_pinmux_config(dev_id, flag); |
110 | if (ret) { | |
111 | printf("external SD not configured\n"); | |
112 | return ret; | |
113 | } | |
114 | ||
0347960b SG |
115 | if (dm_gpio_is_valid(&host->pwr_gpio)) { |
116 | dm_gpio_set_value(&host->pwr_gpio, 1); | |
2308ea7c TJ |
117 | ret = exynos_pinmux_config(dev_id, flag); |
118 | if (ret) { | |
3577fe8b | 119 | debug("MMC not configured\n"); |
2308ea7c | 120 | return ret; |
3577fe8b PW |
121 | } |
122 | } | |
123 | ||
0347960b | 124 | if (dm_gpio_is_valid(&host->cd_gpio)) { |
2308ea7c TJ |
125 | ret = dm_gpio_get_value(&host->cd_gpio); |
126 | if (ret) { | |
127 | debug("no SD card detected (%d)\n", ret); | |
3577fe8b | 128 | return -ENODEV; |
2308ea7c | 129 | } |
3577fe8b PW |
130 | } |
131 | ||
9b8c9a3c | 132 | return s5p_sdhci_core_init(host); |
3577fe8b PW |
133 | } |
134 | ||
135 | static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) | |
136 | { | |
137 | int bus_width, dev_id; | |
138 | unsigned int base; | |
139 | ||
140 | /* Get device id */ | |
141 | dev_id = pinmux_decode_periph_id(blob, node); | |
142 | if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { | |
143 | debug("MMC: Can't get device id\n"); | |
144 | return -1; | |
145 | } | |
146 | host->index = dev_id - PERIPH_ID_SDMMC0; | |
147 | ||
148 | /* Get bus width */ | |
149 | bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); | |
150 | if (bus_width <= 0) { | |
151 | debug("MMC: Can't get bus-width\n"); | |
152 | return -1; | |
153 | } | |
154 | host->bus_width = bus_width; | |
155 | ||
156 | /* Get the base address from the device node */ | |
157 | base = fdtdec_get_addr(blob, node, "reg"); | |
158 | if (!base) { | |
159 | debug("MMC: Can't get base address\n"); | |
160 | return -1; | |
161 | } | |
162 | host->ioaddr = (void *)base; | |
163 | ||
0347960b SG |
164 | gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, |
165 | GPIOD_IS_OUT); | |
166 | gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, | |
167 | GPIOD_IS_IN); | |
3577fe8b PW |
168 | |
169 | return 0; | |
170 | } | |
171 | ||
172 | static int process_nodes(const void *blob, int node_list[], int count) | |
173 | { | |
174 | struct sdhci_host *host; | |
995a54cc | 175 | int i, node, ret; |
6a9fbb6e | 176 | int failed = 0; |
3577fe8b PW |
177 | |
178 | debug("%s: count = %d\n", __func__, count); | |
179 | ||
180 | /* build sdhci_host[] for each controller */ | |
181 | for (i = 0; i < count; i++) { | |
182 | node = node_list[i]; | |
183 | if (node <= 0) | |
184 | continue; | |
185 | ||
186 | host = &sdhci_host[i]; | |
187 | ||
995a54cc TJ |
188 | ret = sdhci_get_config(blob, node, host); |
189 | if (ret) { | |
190 | printf("%s: failed to decode dev %d (%d)\n", __func__, i, ret); | |
6a9fbb6e TJ |
191 | failed++; |
192 | continue; | |
193 | } | |
194 | ||
995a54cc | 195 | ret = do_sdhci_init(host); |
96094d4c | 196 | if (ret && ret != -ENODEV) { |
995a54cc | 197 | printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret); |
6a9fbb6e | 198 | failed++; |
3577fe8b | 199 | } |
3577fe8b | 200 | } |
6a9fbb6e TJ |
201 | |
202 | /* we only consider it an error when all nodes fail */ | |
203 | return (failed == count ? -1 : 0); | |
3577fe8b PW |
204 | } |
205 | ||
206 | int exynos_mmc_init(const void *blob) | |
207 | { | |
208 | int count; | |
209 | int node_list[SDHCI_MAX_HOSTS]; | |
210 | ||
211 | count = fdtdec_find_aliases_for_id(blob, "mmc", | |
212 | COMPAT_SAMSUNG_EXYNOS_MMC, node_list, | |
213 | SDHCI_MAX_HOSTS); | |
214 | ||
6a9fbb6e | 215 | return process_nodes(blob, node_list, count); |
3577fe8b PW |
216 | } |
217 | #endif |