]>
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 | |
13243f2e | 71 | SDHCI_QUIRK_BROKEN_R1B | 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 | { | |
87 | struct sdhci_host *host = malloc(sizeof(struct sdhci_host)); | |
88 | if (!host) { | |
89 | printf("sdhci__host malloc fail!\n"); | |
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 | { | |
104 | int dev_id, flag; | |
105 | int err = 0; | |
106 | ||
107 | flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; | |
108 | dev_id = host->index + PERIPH_ID_SDMMC0; | |
109 | ||
0347960b SG |
110 | if (dm_gpio_is_valid(&host->pwr_gpio)) { |
111 | dm_gpio_set_value(&host->pwr_gpio, 1); | |
3577fe8b PW |
112 | err = exynos_pinmux_config(dev_id, flag); |
113 | if (err) { | |
114 | debug("MMC not configured\n"); | |
115 | return err; | |
116 | } | |
117 | } | |
118 | ||
0347960b SG |
119 | if (dm_gpio_is_valid(&host->cd_gpio)) { |
120 | if (dm_gpio_get_value(&host->cd_gpio)) | |
3577fe8b PW |
121 | return -ENODEV; |
122 | ||
123 | err = exynos_pinmux_config(dev_id, flag); | |
124 | if (err) { | |
125 | printf("external SD not configured\n"); | |
126 | return err; | |
127 | } | |
128 | } | |
129 | ||
9b8c9a3c | 130 | return s5p_sdhci_core_init(host); |
3577fe8b PW |
131 | } |
132 | ||
133 | static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) | |
134 | { | |
135 | int bus_width, dev_id; | |
136 | unsigned int base; | |
137 | ||
138 | /* Get device id */ | |
139 | dev_id = pinmux_decode_periph_id(blob, node); | |
140 | if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { | |
141 | debug("MMC: Can't get device id\n"); | |
142 | return -1; | |
143 | } | |
144 | host->index = dev_id - PERIPH_ID_SDMMC0; | |
145 | ||
146 | /* Get bus width */ | |
147 | bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); | |
148 | if (bus_width <= 0) { | |
149 | debug("MMC: Can't get bus-width\n"); | |
150 | return -1; | |
151 | } | |
152 | host->bus_width = bus_width; | |
153 | ||
154 | /* Get the base address from the device node */ | |
155 | base = fdtdec_get_addr(blob, node, "reg"); | |
156 | if (!base) { | |
157 | debug("MMC: Can't get base address\n"); | |
158 | return -1; | |
159 | } | |
160 | host->ioaddr = (void *)base; | |
161 | ||
0347960b SG |
162 | gpio_request_by_name_nodev(blob, node, "pwr-gpios", 0, &host->pwr_gpio, |
163 | GPIOD_IS_OUT); | |
164 | gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, | |
165 | GPIOD_IS_IN); | |
3577fe8b PW |
166 | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static int process_nodes(const void *blob, int node_list[], int count) | |
171 | { | |
172 | struct sdhci_host *host; | |
173 | int i, node; | |
174 | ||
175 | debug("%s: count = %d\n", __func__, count); | |
176 | ||
177 | /* build sdhci_host[] for each controller */ | |
178 | for (i = 0; i < count; i++) { | |
179 | node = node_list[i]; | |
180 | if (node <= 0) | |
181 | continue; | |
182 | ||
183 | host = &sdhci_host[i]; | |
184 | ||
185 | if (sdhci_get_config(blob, node, host)) { | |
186 | printf("%s: failed to decode dev %d\n", __func__, i); | |
187 | return -1; | |
188 | } | |
189 | do_sdhci_init(host); | |
190 | } | |
191 | return 0; | |
192 | } | |
193 | ||
194 | int exynos_mmc_init(const void *blob) | |
195 | { | |
196 | int count; | |
197 | int node_list[SDHCI_MAX_HOSTS]; | |
198 | ||
199 | count = fdtdec_find_aliases_for_id(blob, "mmc", | |
200 | COMPAT_SAMSUNG_EXYNOS_MMC, node_list, | |
201 | SDHCI_MAX_HOSTS); | |
202 | ||
203 | process_nodes(blob, node_list, count); | |
204 | ||
c95ed7d9 | 205 | return 0; |
3577fe8b PW |
206 | } |
207 | #endif |