]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ab693e9c MK |
2 | /* |
3 | * (C) Copyright 2009 Samsung Electronics | |
4 | * Minkyu Kang <[email protected]> | |
ab693e9c MK |
5 | */ |
6 | ||
7 | #include <common.h> | |
b8809e60 SG |
8 | #include <dm.h> |
9 | #include <errno.h> | |
10 | #include <fdtdec.h> | |
f7ae49fc | 11 | #include <log.h> |
b8809e60 | 12 | #include <malloc.h> |
ab693e9c | 13 | #include <asm/io.h> |
365d6070 | 14 | #include <asm/gpio.h> |
b8809e60 SG |
15 | #include <dm/device-internal.h> |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
ab693e9c | 18 | |
f6ae1ca0 | 19 | #define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK) |
8475c869 | 20 | |
3a233dbf SG |
21 | #define CON_MASK(val) (0xf << ((val) << 2)) |
22 | #define CON_SFR(gpio, cfg) ((cfg) << ((gpio) << 2)) | |
23 | #define CON_SFR_UNSHIFT(val, gpio) ((val) >> ((gpio) << 2)) | |
ab693e9c | 24 | |
3a233dbf SG |
25 | #define DAT_MASK(gpio) (0x1 << (gpio)) |
26 | #define DAT_SET(gpio) (0x1 << (gpio)) | |
ab693e9c | 27 | |
3a233dbf SG |
28 | #define PULL_MASK(gpio) (0x3 << ((gpio) << 1)) |
29 | #define PULL_MODE(gpio, pull) ((pull) << ((gpio) << 1)) | |
ab693e9c | 30 | |
3a233dbf SG |
31 | #define DRV_MASK(gpio) (0x3 << ((gpio) << 1)) |
32 | #define DRV_SET(gpio, mode) ((mode) << ((gpio) << 1)) | |
33 | #define RATE_MASK(gpio) (0x1 << (gpio + 16)) | |
34 | #define RATE_SET(gpio) (0x1 << (gpio + 16)) | |
ab693e9c | 35 | |
b8809e60 SG |
36 | /* Platform data for each bank */ |
37 | struct exynos_gpio_platdata { | |
38 | struct s5p_gpio_bank *bank; | |
39 | const char *bank_name; /* Name of port, e.g. 'gpa0" */ | |
40 | }; | |
41 | ||
42 | /* Information about each bank at run-time */ | |
43 | struct exynos_bank_info { | |
b8809e60 SG |
44 | struct s5p_gpio_bank *bank; |
45 | }; | |
46 | ||
47 | static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned int gpio) | |
f6ae1ca0 | 48 | { |
b8809e60 SG |
49 | const struct gpio_info *data; |
50 | unsigned int upto; | |
51 | int i, count; | |
f6ae1ca0 | 52 | |
b8809e60 SG |
53 | data = get_gpio_data(); |
54 | count = get_bank_num(); | |
55 | upto = 0; | |
56 | ||
57 | for (i = 0; i < count; i++) { | |
58 | debug("i=%d, upto=%d\n", i, upto); | |
59 | if (gpio < data->max_gpio) { | |
60 | struct s5p_gpio_bank *bank; | |
61 | bank = (struct s5p_gpio_bank *)data->reg_addr; | |
62 | bank += (gpio - upto) / GPIO_PER_BANK; | |
63 | debug("gpio=%d, bank=%p\n", gpio, bank); | |
64 | return bank; | |
f6ae1ca0 | 65 | } |
f6ae1ca0 | 66 | |
b8809e60 SG |
67 | upto = data->max_gpio; |
68 | data++; | |
69 | } | |
70 | ||
71 | return NULL; | |
f6ae1ca0 AS |
72 | } |
73 | ||
74 | static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) | |
ab693e9c MK |
75 | { |
76 | unsigned int value; | |
77 | ||
78 | value = readl(&bank->con); | |
79 | value &= ~CON_MASK(gpio); | |
80 | value |= CON_SFR(gpio, cfg); | |
81 | writel(value, &bank->con); | |
82 | } | |
83 | ||
f6ae1ca0 | 84 | static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) |
ab693e9c MK |
85 | { |
86 | unsigned int value; | |
87 | ||
88 | value = readl(&bank->dat); | |
89 | value &= ~DAT_MASK(gpio); | |
90 | if (en) | |
91 | value |= DAT_SET(gpio); | |
92 | writel(value, &bank->dat); | |
93 | } | |
94 | ||
b8809e60 SG |
95 | #ifdef CONFIG_SPL_BUILD |
96 | /* Common GPIO API - SPL does not support driver model yet */ | |
97 | int gpio_set_value(unsigned gpio, int value) | |
f6ae1ca0 | 98 | { |
b8809e60 SG |
99 | s5p_gpio_set_value(s5p_gpio_get_bank(gpio), |
100 | s5p_gpio_get_pin(gpio), value); | |
f6ae1ca0 | 101 | |
b8809e60 SG |
102 | return 0; |
103 | } | |
104 | #else | |
105 | static int s5p_gpio_get_cfg_pin(struct s5p_gpio_bank *bank, int gpio) | |
f6ae1ca0 | 106 | { |
b8809e60 SG |
107 | unsigned int value; |
108 | ||
109 | value = readl(&bank->con); | |
110 | value &= CON_MASK(gpio); | |
111 | return CON_SFR_UNSHIFT(value, gpio); | |
f6ae1ca0 AS |
112 | } |
113 | ||
114 | static unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) | |
ab693e9c MK |
115 | { |
116 | unsigned int value; | |
117 | ||
118 | value = readl(&bank->dat); | |
119 | return !!(value & DAT_MASK(gpio)); | |
120 | } | |
b8809e60 | 121 | #endif /* CONFIG_SPL_BUILD */ |
ab693e9c | 122 | |
f6ae1ca0 | 123 | static void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) |
ab693e9c MK |
124 | { |
125 | unsigned int value; | |
126 | ||
127 | value = readl(&bank->pull); | |
128 | value &= ~PULL_MASK(gpio); | |
129 | ||
130 | switch (mode) { | |
f6ae1ca0 AS |
131 | case S5P_GPIO_PULL_DOWN: |
132 | case S5P_GPIO_PULL_UP: | |
ab693e9c MK |
133 | value |= PULL_MODE(gpio, mode); |
134 | break; | |
135 | default: | |
ffb4b025 | 136 | break; |
ab693e9c MK |
137 | } |
138 | ||
139 | writel(value, &bank->pull); | |
140 | } | |
141 | ||
f6ae1ca0 | 142 | static void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) |
ab693e9c MK |
143 | { |
144 | unsigned int value; | |
145 | ||
146 | value = readl(&bank->drv); | |
147 | value &= ~DRV_MASK(gpio); | |
148 | ||
149 | switch (mode) { | |
f6ae1ca0 AS |
150 | case S5P_GPIO_DRV_1X: |
151 | case S5P_GPIO_DRV_2X: | |
152 | case S5P_GPIO_DRV_3X: | |
153 | case S5P_GPIO_DRV_4X: | |
ab693e9c MK |
154 | value |= DRV_SET(gpio, mode); |
155 | break; | |
156 | default: | |
157 | return; | |
158 | } | |
159 | ||
160 | writel(value, &bank->drv); | |
161 | } | |
162 | ||
f6ae1ca0 | 163 | static void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) |
ab693e9c MK |
164 | { |
165 | unsigned int value; | |
166 | ||
167 | value = readl(&bank->drv); | |
168 | value &= ~RATE_MASK(gpio); | |
169 | ||
170 | switch (mode) { | |
f6ae1ca0 AS |
171 | case S5P_GPIO_DRV_FAST: |
172 | case S5P_GPIO_DRV_SLOW: | |
ab693e9c MK |
173 | value |= RATE_SET(gpio); |
174 | break; | |
175 | default: | |
176 | return; | |
177 | } | |
178 | ||
179 | writel(value, &bank->drv); | |
180 | } | |
9f15bc0c | 181 | |
b8809e60 | 182 | int s5p_gpio_get_pin(unsigned gpio) |
9f15bc0c | 183 | { |
b8809e60 SG |
184 | return S5P_GPIO_GET_PIN(gpio); |
185 | } | |
f6ae1ca0 | 186 | |
b8809e60 SG |
187 | /* Driver model interface */ |
188 | #ifndef CONFIG_SPL_BUILD | |
b8809e60 SG |
189 | /* set GPIO pin 'gpio' as an input */ |
190 | static int exynos_gpio_direction_input(struct udevice *dev, unsigned offset) | |
9f15bc0c | 191 | { |
b8809e60 | 192 | struct exynos_bank_info *state = dev_get_priv(dev); |
b8809e60 SG |
193 | |
194 | /* Configure GPIO direction as input. */ | |
195 | s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_INPUT); | |
365d6070 | 196 | |
9f15bc0c ŁM |
197 | return 0; |
198 | } | |
199 | ||
b8809e60 SG |
200 | /* set GPIO pin 'gpio' as an output, with polarity 'value' */ |
201 | static int exynos_gpio_direction_output(struct udevice *dev, unsigned offset, | |
202 | int value) | |
9f15bc0c | 203 | { |
b8809e60 | 204 | struct exynos_bank_info *state = dev_get_priv(dev); |
b8809e60 SG |
205 | |
206 | /* Configure GPIO output value. */ | |
207 | s5p_gpio_set_value(state->bank, offset, value); | |
208 | ||
209 | /* Configure GPIO direction as output. */ | |
210 | s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_OUTPUT); | |
211 | ||
9f15bc0c ŁM |
212 | return 0; |
213 | } | |
214 | ||
b8809e60 SG |
215 | /* read GPIO IN value of pin 'gpio' */ |
216 | static int exynos_gpio_get_value(struct udevice *dev, unsigned offset) | |
9f15bc0c | 217 | { |
b8809e60 | 218 | struct exynos_bank_info *state = dev_get_priv(dev); |
b8809e60 SG |
219 | |
220 | return s5p_gpio_get_value(state->bank, offset); | |
9f15bc0c ŁM |
221 | } |
222 | ||
b8809e60 SG |
223 | /* write GPIO OUT value to pin 'gpio' */ |
224 | static int exynos_gpio_set_value(struct udevice *dev, unsigned offset, | |
225 | int value) | |
9f15bc0c | 226 | { |
b8809e60 | 227 | struct exynos_bank_info *state = dev_get_priv(dev); |
b8809e60 SG |
228 | |
229 | s5p_gpio_set_value(state->bank, offset, value); | |
230 | ||
365d6070 | 231 | return 0; |
9f15bc0c | 232 | } |
b8809e60 | 233 | #endif /* nCONFIG_SPL_BUILD */ |
f6ae1ca0 | 234 | |
b8809e60 SG |
235 | /* |
236 | * There is no common GPIO API for pull, drv, pin, rate (yet). These | |
237 | * functions are kept here to preserve function ordering for review. | |
238 | */ | |
f6ae1ca0 AS |
239 | void gpio_set_pull(int gpio, int mode) |
240 | { | |
241 | s5p_gpio_set_pull(s5p_gpio_get_bank(gpio), | |
242 | s5p_gpio_get_pin(gpio), mode); | |
243 | } | |
244 | ||
245 | void gpio_set_drv(int gpio, int mode) | |
246 | { | |
247 | s5p_gpio_set_drv(s5p_gpio_get_bank(gpio), | |
248 | s5p_gpio_get_pin(gpio), mode); | |
249 | } | |
250 | ||
251 | void gpio_cfg_pin(int gpio, int cfg) | |
252 | { | |
253 | s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio), | |
254 | s5p_gpio_get_pin(gpio), cfg); | |
255 | } | |
256 | ||
257 | void gpio_set_rate(int gpio, int mode) | |
258 | { | |
259 | s5p_gpio_set_rate(s5p_gpio_get_bank(gpio), | |
260 | s5p_gpio_get_pin(gpio), mode); | |
261 | } | |
b8809e60 SG |
262 | |
263 | #ifndef CONFIG_SPL_BUILD | |
264 | static int exynos_gpio_get_function(struct udevice *dev, unsigned offset) | |
265 | { | |
266 | struct exynos_bank_info *state = dev_get_priv(dev); | |
267 | int cfg; | |
268 | ||
b8809e60 SG |
269 | cfg = s5p_gpio_get_cfg_pin(state->bank, offset); |
270 | if (cfg == S5P_GPIO_OUTPUT) | |
271 | return GPIOF_OUTPUT; | |
272 | else if (cfg == S5P_GPIO_INPUT) | |
273 | return GPIOF_INPUT; | |
274 | else | |
275 | return GPIOF_FUNC; | |
276 | } | |
277 | ||
278 | static const struct dm_gpio_ops gpio_exynos_ops = { | |
b8809e60 SG |
279 | .direction_input = exynos_gpio_direction_input, |
280 | .direction_output = exynos_gpio_direction_output, | |
281 | .get_value = exynos_gpio_get_value, | |
282 | .set_value = exynos_gpio_set_value, | |
283 | .get_function = exynos_gpio_get_function, | |
b8809e60 SG |
284 | }; |
285 | ||
286 | static int gpio_exynos_probe(struct udevice *dev) | |
287 | { | |
e564f054 | 288 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
b8809e60 | 289 | struct exynos_bank_info *priv = dev->priv; |
caa4daa2 | 290 | struct exynos_gpio_platdata *plat = dev->plat; |
b8809e60 SG |
291 | |
292 | /* Only child devices have ports */ | |
293 | if (!plat) | |
294 | return 0; | |
295 | ||
296 | priv->bank = plat->bank; | |
297 | ||
298 | uc_priv->gpio_count = GPIO_PER_BANK; | |
299 | uc_priv->bank_name = plat->bank_name; | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | /** | |
305 | * We have a top-level GPIO device with no actual GPIOs. It has a child | |
306 | * device for each Exynos GPIO bank. | |
307 | */ | |
308 | static int gpio_exynos_bind(struct udevice *parent) | |
309 | { | |
caa4daa2 | 310 | struct exynos_gpio_platdata *plat = parent->plat; |
b8809e60 SG |
311 | struct s5p_gpio_bank *bank, *base; |
312 | const void *blob = gd->fdt_blob; | |
313 | int node; | |
314 | ||
315 | /* If this is a child device, there is nothing to do here */ | |
316 | if (plat) | |
317 | return 0; | |
318 | ||
8613c8d8 | 319 | base = dev_read_addr_ptr(parent); |
e160f7d4 | 320 | for (node = fdt_first_subnode(blob, dev_of_offset(parent)), bank = base; |
b8809e60 SG |
321 | node > 0; |
322 | node = fdt_next_subnode(blob, node), bank++) { | |
323 | struct exynos_gpio_platdata *plat; | |
324 | struct udevice *dev; | |
325 | fdt_addr_t reg; | |
326 | int ret; | |
327 | ||
328 | if (!fdtdec_get_bool(blob, node, "gpio-controller")) | |
329 | continue; | |
330 | plat = calloc(1, sizeof(*plat)); | |
331 | if (!plat) | |
332 | return -ENOMEM; | |
b8809e60 | 333 | |
6f183e86 | 334 | plat->bank_name = fdt_get_name(blob, node, NULL); |
a2703ce1 | 335 | ret = device_bind(parent, parent->driver, plat->bank_name, plat, |
20da4e02 | 336 | offset_to_ofnode(node), &dev); |
b8809e60 SG |
337 | if (ret) |
338 | return ret; | |
6f183e86 | 339 | |
2548493a | 340 | reg = dev_read_addr(dev); |
6f183e86 PM |
341 | if (reg != FDT_ADDR_T_NONE) |
342 | bank = (struct s5p_gpio_bank *)((ulong)base + reg); | |
343 | ||
344 | plat->bank = bank; | |
345 | ||
346 | debug("dev at %p: %s\n", bank, plat->bank_name); | |
b8809e60 SG |
347 | } |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
352 | static const struct udevice_id exynos_gpio_ids[] = { | |
353 | { .compatible = "samsung,s5pc100-pinctrl" }, | |
354 | { .compatible = "samsung,s5pc110-pinctrl" }, | |
355 | { .compatible = "samsung,exynos4210-pinctrl" }, | |
356 | { .compatible = "samsung,exynos4x12-pinctrl" }, | |
357 | { .compatible = "samsung,exynos5250-pinctrl" }, | |
358 | { .compatible = "samsung,exynos5420-pinctrl" }, | |
359 | { } | |
360 | }; | |
361 | ||
362 | U_BOOT_DRIVER(gpio_exynos) = { | |
363 | .name = "gpio_exynos", | |
364 | .id = UCLASS_GPIO, | |
365 | .of_match = exynos_gpio_ids, | |
366 | .bind = gpio_exynos_bind, | |
367 | .probe = gpio_exynos_probe, | |
41575d8e | 368 | .priv_auto = sizeof(struct exynos_bank_info), |
b8809e60 SG |
369 | .ops = &gpio_exynos_ops, |
370 | }; | |
371 | #endif |