Commit 3577fe8be9dc8c8aa027361d6424efba9f97f553
Committed by
Minkyu Kang
1 parent
1591ee7352
Exists in
v2017.01-smarct4x
and in
48 other branches
drivers:mmc:sdhci: enable support for DT
This patch enables support for device tree for sdhci driver. Non DT case is still supported. Signed-off-by: Piotr Wilczek <p.wilczek@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Showing 5 changed files with 143 additions and 0 deletions Side-by-side Diff
arch/arm/include/asm/arch-exynos/mmc.h
... | ... | @@ -53,6 +53,8 @@ |
53 | 53 | #define SDHCI_CTRL4_DRIVE_MASK(_x) ((_x) << 16) |
54 | 54 | #define SDHCI_CTRL4_DRIVE_SHIFT (16) |
55 | 55 | |
56 | +#define SDHCI_MAX_HOSTS 4 | |
57 | + | |
56 | 58 | int s5p_sdhci_init(u32 regbase, int index, int bus_width); |
57 | 59 | |
58 | 60 | static inline int s5p_mmc_init(int index, int bus_width) |
... | ... | @@ -62,5 +64,10 @@ |
62 | 64 | |
63 | 65 | return s5p_sdhci_init(base, index, bus_width); |
64 | 66 | } |
67 | + | |
68 | +#ifdef CONFIG_OF_CONTROL | |
69 | +int exynos_mmc_init(const void *blob); | |
70 | +#endif | |
71 | + | |
65 | 72 | #endif |
drivers/mmc/s5p_sdhci.c
... | ... | @@ -8,8 +8,15 @@ |
8 | 8 | #include <common.h> |
9 | 9 | #include <malloc.h> |
10 | 10 | #include <sdhci.h> |
11 | +#include <fdtdec.h> | |
12 | +#include <libfdt.h> | |
13 | +#include <asm/gpio.h> | |
11 | 14 | #include <asm/arch/mmc.h> |
12 | 15 | #include <asm/arch/clk.h> |
16 | +#include <errno.h> | |
17 | +#ifdef CONFIG_OF_CONTROL | |
18 | +#include <asm/arch/pinmux.h> | |
19 | +#endif | |
13 | 20 | |
14 | 21 | static char *S5P_NAME = "SAMSUNG SDHCI"; |
15 | 22 | static void s5p_sdhci_set_control_reg(struct sdhci_host *host) |
... | ... | @@ -86,4 +93,126 @@ |
86 | 93 | |
87 | 94 | return add_sdhci(host, 52000000, 400000); |
88 | 95 | } |
96 | + | |
97 | +#ifdef CONFIG_OF_CONTROL | |
98 | +struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS]; | |
99 | + | |
100 | +static int do_sdhci_init(struct sdhci_host *host) | |
101 | +{ | |
102 | + int dev_id, flag; | |
103 | + int err = 0; | |
104 | + | |
105 | + flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE; | |
106 | + dev_id = host->index + PERIPH_ID_SDMMC0; | |
107 | + | |
108 | + if (fdt_gpio_isvalid(&host->pwr_gpio)) { | |
109 | + gpio_direction_output(host->pwr_gpio.gpio, 1); | |
110 | + err = exynos_pinmux_config(dev_id, flag); | |
111 | + if (err) { | |
112 | + debug("MMC not configured\n"); | |
113 | + return err; | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + if (fdt_gpio_isvalid(&host->cd_gpio)) { | |
118 | + gpio_direction_output(host->cd_gpio.gpio, 0xf); | |
119 | + if (gpio_get_value(host->cd_gpio.gpio)) | |
120 | + return -ENODEV; | |
121 | + | |
122 | + err = exynos_pinmux_config(dev_id, flag); | |
123 | + if (err) { | |
124 | + printf("external SD not configured\n"); | |
125 | + return err; | |
126 | + } | |
127 | + } | |
128 | + | |
129 | + host->name = S5P_NAME; | |
130 | + | |
131 | + host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | | |
132 | + SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | | |
133 | + SDHCI_QUIRK_WAIT_SEND_CMD; | |
134 | + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; | |
135 | + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); | |
136 | + | |
137 | + host->set_control_reg = &s5p_sdhci_set_control_reg; | |
138 | + host->set_clock = set_mmc_clk; | |
139 | + | |
140 | + host->host_caps = MMC_MODE_HC; | |
141 | + | |
142 | + return add_sdhci(host, 52000000, 400000); | |
143 | +} | |
144 | + | |
145 | +static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host) | |
146 | +{ | |
147 | + int bus_width, dev_id; | |
148 | + unsigned int base; | |
149 | + | |
150 | + /* Get device id */ | |
151 | + dev_id = pinmux_decode_periph_id(blob, node); | |
152 | + if (dev_id < PERIPH_ID_SDMMC0 && dev_id > PERIPH_ID_SDMMC3) { | |
153 | + debug("MMC: Can't get device id\n"); | |
154 | + return -1; | |
155 | + } | |
156 | + host->index = dev_id - PERIPH_ID_SDMMC0; | |
157 | + | |
158 | + /* Get bus width */ | |
159 | + bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); | |
160 | + if (bus_width <= 0) { | |
161 | + debug("MMC: Can't get bus-width\n"); | |
162 | + return -1; | |
163 | + } | |
164 | + host->bus_width = bus_width; | |
165 | + | |
166 | + /* Get the base address from the device node */ | |
167 | + base = fdtdec_get_addr(blob, node, "reg"); | |
168 | + if (!base) { | |
169 | + debug("MMC: Can't get base address\n"); | |
170 | + return -1; | |
171 | + } | |
172 | + host->ioaddr = (void *)base; | |
173 | + | |
174 | + fdtdec_decode_gpio(blob, node, "pwr-gpios", &host->pwr_gpio); | |
175 | + fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); | |
176 | + | |
177 | + return 0; | |
178 | +} | |
179 | + | |
180 | +static int process_nodes(const void *blob, int node_list[], int count) | |
181 | +{ | |
182 | + struct sdhci_host *host; | |
183 | + int i, node; | |
184 | + | |
185 | + debug("%s: count = %d\n", __func__, count); | |
186 | + | |
187 | + /* build sdhci_host[] for each controller */ | |
188 | + for (i = 0; i < count; i++) { | |
189 | + node = node_list[i]; | |
190 | + if (node <= 0) | |
191 | + continue; | |
192 | + | |
193 | + host = &sdhci_host[i]; | |
194 | + | |
195 | + if (sdhci_get_config(blob, node, host)) { | |
196 | + printf("%s: failed to decode dev %d\n", __func__, i); | |
197 | + return -1; | |
198 | + } | |
199 | + do_sdhci_init(host); | |
200 | + } | |
201 | + return 0; | |
202 | +} | |
203 | + | |
204 | +int exynos_mmc_init(const void *blob) | |
205 | +{ | |
206 | + int count; | |
207 | + int node_list[SDHCI_MAX_HOSTS]; | |
208 | + | |
209 | + count = fdtdec_find_aliases_for_id(blob, "mmc", | |
210 | + COMPAT_SAMSUNG_EXYNOS_MMC, node_list, | |
211 | + SDHCI_MAX_HOSTS); | |
212 | + | |
213 | + process_nodes(blob, node_list, count); | |
214 | + | |
215 | + return 1; | |
216 | +} | |
217 | +#endif |
include/fdtdec.h
... | ... | @@ -82,6 +82,7 @@ |
82 | 82 | COMPAT_SAMSUNG_EXYNOS_MIPI_DSI, /* Exynos mipi dsi */ |
83 | 83 | COMPAT_SAMSUNG_EXYNOS5_DP, /* Exynos Display port controller */ |
84 | 84 | COMPAT_SAMSUNG_EXYNOS5_DWMMC, /* Exynos5 DWMMC controller */ |
85 | + COMPAT_SAMSUNG_EXYNOS_MMC, /* Exynos MMC controller */ | |
85 | 86 | COMPAT_SAMSUNG_EXYNOS_SERIAL, /* Exynos UART */ |
86 | 87 | COMPAT_MAXIM_MAX77686_PMIC, /* MAX77686 PMIC */ |
87 | 88 | COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ |
include/sdhci.h
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | |
13 | 13 | #include <asm/io.h> |
14 | 14 | #include <mmc.h> |
15 | +#include <fdtdec.h> | |
15 | 16 | |
16 | 17 | /* |
17 | 18 | * Controller registers |
... | ... | @@ -243,6 +244,10 @@ |
243 | 244 | struct mmc *mmc; |
244 | 245 | const struct sdhci_ops *ops; |
245 | 246 | int index; |
247 | + | |
248 | + int bus_width; | |
249 | + struct fdt_gpio_state pwr_gpio; /* Power GPIO */ | |
250 | + struct fdt_gpio_state cd_gpio; /* Card Detect GPIO */ | |
246 | 251 | |
247 | 252 | void (*set_control_reg)(struct sdhci_host *host); |
248 | 253 | void (*set_clock)(int dev_index, unsigned int div); |
lib/fdtdec.c
... | ... | @@ -55,6 +55,7 @@ |
55 | 55 | COMPAT(SAMSUNG_EXYNOS_MIPI_DSI, "samsung,exynos-mipi-dsi"), |
56 | 56 | COMPAT(SAMSUNG_EXYNOS5_DP, "samsung,exynos5-dp"), |
57 | 57 | COMPAT(SAMSUNG_EXYNOS5_DWMMC, "samsung,exynos5250-dwmmc"), |
58 | + COMPAT(SAMSUNG_EXYNOS_MMC, "samsung,exynos-mmc"), | |
58 | 59 | COMPAT(SAMSUNG_EXYNOS_SERIAL, "samsung,exynos4210-uart"), |
59 | 60 | COMPAT(MAXIM_MAX77686_PMIC, "maxim,max77686_pmic"), |
60 | 61 | COMPAT(GENERIC_SPI_FLASH, "spi-flash"), |