]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
677b5358 BG |
2 | /* |
3 | * (C) Copyright 2016 - Beniamino Galvani <b.galvani@gmail.com> | |
677b5358 BG |
4 | */ |
5 | ||
6 | #include <common.h> | |
9d922450 | 7 | #include <dm.h> |
336d4615 | 8 | #include <malloc.h> |
2009a8d0 | 9 | #include <dm/device-internal.h> |
336d4615 | 10 | #include <dm/device_compat.h> |
2009a8d0 | 11 | #include <dm/lists.h> |
677b5358 BG |
12 | #include <dm/pinctrl.h> |
13 | #include <fdt_support.h> | |
14 | #include <linux/err.h> | |
15 | #include <linux/io.h> | |
4d72caa5 | 16 | #include <linux/libfdt.h> |
677b5358 | 17 | #include <linux/sizes.h> |
2009a8d0 | 18 | #include <asm/gpio.h> |
677b5358 BG |
19 | |
20 | #include "pinctrl-meson.h" | |
21 | ||
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
24 | static const char *meson_pinctrl_dummy_name = "_dummy"; | |
25 | ||
88fa32b8 NA |
26 | static char pin_name[PINNAME_SIZE]; |
27 | ||
7c9dcfed | 28 | int meson_pinctrl_get_groups_count(struct udevice *dev) |
677b5358 BG |
29 | { |
30 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
31 | ||
32 | return priv->data->num_groups; | |
33 | } | |
34 | ||
7c9dcfed JB |
35 | const char *meson_pinctrl_get_group_name(struct udevice *dev, |
36 | unsigned int selector) | |
677b5358 BG |
37 | { |
38 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
39 | ||
40 | if (!priv->data->groups[selector].name) | |
41 | return meson_pinctrl_dummy_name; | |
42 | ||
43 | return priv->data->groups[selector].name; | |
44 | } | |
45 | ||
88fa32b8 NA |
46 | int meson_pinctrl_get_pins_count(struct udevice *dev) |
47 | { | |
48 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
49 | ||
50 | return priv->data->num_pins; | |
51 | } | |
52 | ||
53 | const char *meson_pinctrl_get_pin_name(struct udevice *dev, | |
54 | unsigned int selector) | |
55 | { | |
56 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
57 | ||
58 | if (selector > priv->data->num_pins || | |
59 | selector > priv->data->funcs[0].num_groups) | |
60 | snprintf(pin_name, PINNAME_SIZE, "Error"); | |
61 | else | |
62 | snprintf(pin_name, PINNAME_SIZE, "%s", | |
63 | priv->data->funcs[0].groups[selector]); | |
64 | ||
65 | return pin_name; | |
66 | } | |
67 | ||
7c9dcfed | 68 | int meson_pinmux_get_functions_count(struct udevice *dev) |
677b5358 BG |
69 | { |
70 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
71 | ||
72 | return priv->data->num_funcs; | |
73 | } | |
74 | ||
7c9dcfed JB |
75 | const char *meson_pinmux_get_function_name(struct udevice *dev, |
76 | unsigned int selector) | |
677b5358 BG |
77 | { |
78 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
79 | ||
80 | return priv->data->funcs[selector].name; | |
81 | } | |
82 | ||
2009a8d0 BG |
83 | static int meson_gpio_calc_reg_and_bit(struct udevice *dev, unsigned int offset, |
84 | enum meson_reg_type reg_type, | |
85 | unsigned int *reg, unsigned int *bit) | |
86 | { | |
c4c726c2 | 87 | struct meson_pinctrl *priv = dev_get_priv(dev); |
2009a8d0 BG |
88 | struct meson_bank *bank = NULL; |
89 | struct meson_reg_desc *desc; | |
90 | unsigned int pin; | |
91 | int i; | |
92 | ||
93 | pin = priv->data->pin_base + offset; | |
94 | ||
95 | for (i = 0; i < priv->data->num_banks; i++) { | |
96 | if (pin >= priv->data->banks[i].first && | |
97 | pin <= priv->data->banks[i].last) { | |
98 | bank = &priv->data->banks[i]; | |
99 | break; | |
100 | } | |
101 | } | |
102 | ||
103 | if (!bank) | |
104 | return -EINVAL; | |
105 | ||
106 | desc = &bank->regs[reg_type]; | |
107 | *reg = desc->reg * 4; | |
108 | *bit = desc->bit + pin - bank->first; | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
7c9dcfed | 113 | int meson_gpio_get(struct udevice *dev, unsigned int offset) |
2009a8d0 BG |
114 | { |
115 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
116 | unsigned int reg, bit; | |
117 | int ret; | |
118 | ||
c4c726c2 JB |
119 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_IN, ®, |
120 | &bit); | |
2009a8d0 BG |
121 | if (ret) |
122 | return ret; | |
123 | ||
124 | return !!(readl(priv->reg_gpio + reg) & BIT(bit)); | |
125 | } | |
126 | ||
7c9dcfed | 127 | int meson_gpio_set(struct udevice *dev, unsigned int offset, int value) |
2009a8d0 BG |
128 | { |
129 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
130 | unsigned int reg, bit; | |
131 | int ret; | |
132 | ||
c4c726c2 JB |
133 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_OUT, ®, |
134 | &bit); | |
2009a8d0 BG |
135 | if (ret) |
136 | return ret; | |
137 | ||
138 | clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
7c9dcfed | 143 | int meson_gpio_get_direction(struct udevice *dev, unsigned int offset) |
2009a8d0 BG |
144 | { |
145 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
146 | unsigned int reg, bit, val; | |
147 | int ret; | |
148 | ||
c4c726c2 JB |
149 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_DIR, ®, |
150 | &bit); | |
2009a8d0 BG |
151 | if (ret) |
152 | return ret; | |
153 | ||
154 | val = readl(priv->reg_gpio + reg); | |
155 | ||
156 | return (val & BIT(bit)) ? GPIOF_INPUT : GPIOF_OUTPUT; | |
157 | } | |
158 | ||
7c9dcfed | 159 | int meson_gpio_direction_input(struct udevice *dev, unsigned int offset) |
2009a8d0 BG |
160 | { |
161 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
162 | unsigned int reg, bit; | |
163 | int ret; | |
164 | ||
c4c726c2 JB |
165 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_DIR, ®, |
166 | &bit); | |
2009a8d0 BG |
167 | if (ret) |
168 | return ret; | |
169 | ||
fb19c7ba | 170 | setbits_le32(priv->reg_gpio + reg, BIT(bit)); |
2009a8d0 BG |
171 | |
172 | return 0; | |
173 | } | |
174 | ||
7c9dcfed JB |
175 | int meson_gpio_direction_output(struct udevice *dev, |
176 | unsigned int offset, int value) | |
2009a8d0 BG |
177 | { |
178 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
179 | unsigned int reg, bit; | |
180 | int ret; | |
181 | ||
c4c726c2 JB |
182 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_DIR, ®, |
183 | &bit); | |
2009a8d0 BG |
184 | if (ret) |
185 | return ret; | |
186 | ||
fb19c7ba | 187 | clrbits_le32(priv->reg_gpio + reg, BIT(bit)); |
2009a8d0 | 188 | |
c4c726c2 JB |
189 | ret = meson_gpio_calc_reg_and_bit(dev->parent, offset, REG_OUT, ®, |
190 | &bit); | |
2009a8d0 BG |
191 | if (ret) |
192 | return ret; | |
193 | ||
194 | clrsetbits_le32(priv->reg_gpio + reg, BIT(bit), value ? BIT(bit) : 0); | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
c4c726c2 JB |
199 | static int meson_pinconf_bias_set(struct udevice *dev, unsigned int pin, |
200 | unsigned int param) | |
201 | { | |
202 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
203 | unsigned int offset = pin - priv->data->pin_base; | |
204 | unsigned int reg, bit; | |
205 | int ret; | |
206 | ||
207 | ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_PULLEN, ®, &bit); | |
208 | if (ret) | |
209 | return ret; | |
210 | ||
211 | if (param == PIN_CONFIG_BIAS_DISABLE) { | |
212 | clrsetbits_le32(priv->reg_pullen + reg, BIT(bit), 0); | |
213 | return 0; | |
214 | } | |
215 | ||
216 | /* othewise, enable the bias and select level */ | |
217 | clrsetbits_le32(priv->reg_pullen + reg, BIT(bit), 1); | |
218 | ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_PULL, ®, &bit); | |
219 | if (ret) | |
220 | return ret; | |
221 | ||
222 | clrsetbits_le32(priv->reg_pull + reg, BIT(bit), | |
223 | param == PIN_CONFIG_BIAS_PULL_UP); | |
224 | ||
225 | return 0; | |
226 | } | |
227 | ||
478c563b GLR |
228 | static int meson_pinconf_drive_strength_set(struct udevice *dev, |
229 | unsigned int pin, | |
230 | unsigned int drive_strength_ua) | |
231 | { | |
232 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
233 | unsigned int offset = pin - priv->data->pin_base; | |
234 | unsigned int reg, bit; | |
235 | unsigned int ds_val; | |
236 | int ret; | |
237 | ||
238 | if (!priv->reg_ds) { | |
239 | dev_err(dev, "drive-strength-microamp not supported\n"); | |
240 | return -ENOTSUPP; | |
241 | } | |
242 | ||
243 | ret = meson_gpio_calc_reg_and_bit(dev, offset, REG_DS, ®, &bit); | |
244 | if (ret) | |
245 | return ret; | |
246 | ||
247 | bit = bit << 1; | |
248 | ||
249 | if (drive_strength_ua <= 500) { | |
250 | ds_val = MESON_PINCONF_DRV_500UA; | |
251 | } else if (drive_strength_ua <= 2500) { | |
252 | ds_val = MESON_PINCONF_DRV_2500UA; | |
253 | } else if (drive_strength_ua <= 3000) { | |
254 | ds_val = MESON_PINCONF_DRV_3000UA; | |
255 | } else if (drive_strength_ua <= 4000) { | |
256 | ds_val = MESON_PINCONF_DRV_4000UA; | |
257 | } else { | |
258 | dev_warn(dev, | |
259 | "pin %u: invalid drive-strength-microamp : %d , default to 4mA\n", | |
260 | pin, drive_strength_ua); | |
261 | ds_val = MESON_PINCONF_DRV_4000UA; | |
262 | } | |
263 | ||
264 | clrsetbits_le32(priv->reg_ds + reg, 0x3 << bit, ds_val << bit); | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
c4c726c2 JB |
269 | int meson_pinconf_set(struct udevice *dev, unsigned int pin, |
270 | unsigned int param, unsigned int arg) | |
271 | { | |
272 | int ret; | |
273 | ||
274 | switch (param) { | |
275 | case PIN_CONFIG_BIAS_DISABLE: | |
276 | case PIN_CONFIG_BIAS_PULL_UP: | |
277 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
278 | ret = meson_pinconf_bias_set(dev, pin, param); | |
279 | break; | |
478c563b GLR |
280 | case PIN_CONFIG_DRIVE_STRENGTH_UA: |
281 | ret = meson_pinconf_drive_strength_set(dev, pin, arg); | |
282 | break; | |
c4c726c2 JB |
283 | default: |
284 | dev_err(dev, "unsupported configuration parameter %u\n", param); | |
285 | return -EINVAL; | |
286 | } | |
287 | ||
288 | return ret; | |
289 | } | |
290 | ||
291 | int meson_pinconf_group_set(struct udevice *dev, | |
292 | unsigned int group_selector, | |
293 | unsigned int param, unsigned int arg) | |
294 | { | |
295 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
296 | struct meson_pmx_group *grp = &priv->data->groups[group_selector]; | |
297 | int i, ret; | |
298 | ||
299 | for (i = 0; i < grp->num_pins; i++) { | |
300 | ret = meson_pinconf_set(dev, grp->pins[i], param, arg); | |
301 | if (ret) | |
302 | return ret; | |
303 | } | |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
7c9dcfed | 308 | int meson_gpio_probe(struct udevice *dev) |
2009a8d0 BG |
309 | { |
310 | struct meson_pinctrl *priv = dev_get_priv(dev->parent); | |
311 | struct gpio_dev_priv *uc_priv; | |
312 | ||
313 | uc_priv = dev_get_uclass_priv(dev); | |
314 | uc_priv->bank_name = priv->data->name; | |
315 | uc_priv->gpio_count = priv->data->num_pins; | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
677b5358 BG |
320 | static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) |
321 | { | |
322 | int index, len = 0; | |
323 | const fdt32_t *reg; | |
324 | ||
b02e4044 | 325 | index = fdt_stringlist_search(gd->fdt_blob, offset, "reg-names", name); |
677b5358 BG |
326 | if (index < 0) |
327 | return FDT_ADDR_T_NONE; | |
328 | ||
329 | reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len); | |
330 | if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) | |
331 | return FDT_ADDR_T_NONE; | |
332 | ||
333 | reg += index * (na + ns); | |
334 | ||
335 | return fdt_translate_address((void *)gd->fdt_blob, offset, reg); | |
336 | } | |
337 | ||
338 | int meson_pinctrl_probe(struct udevice *dev) | |
339 | { | |
340 | struct meson_pinctrl *priv = dev_get_priv(dev); | |
2009a8d0 BG |
341 | struct uclass_driver *drv; |
342 | struct udevice *gpio_dev; | |
677b5358 BG |
343 | fdt_addr_t addr; |
344 | int node, gpio = -1, len; | |
345 | int na, ns; | |
2009a8d0 | 346 | char *name; |
677b5358 | 347 | |
e160f7d4 | 348 | na = fdt_address_cells(gd->fdt_blob, dev_of_offset(dev->parent)); |
677b5358 BG |
349 | if (na < 1) { |
350 | debug("bad #address-cells\n"); | |
351 | return -EINVAL; | |
352 | } | |
353 | ||
e160f7d4 | 354 | ns = fdt_size_cells(gd->fdt_blob, dev_of_offset(dev->parent)); |
677b5358 BG |
355 | if (ns < 1) { |
356 | debug("bad #size-cells\n"); | |
357 | return -EINVAL; | |
358 | } | |
359 | ||
e160f7d4 | 360 | fdt_for_each_subnode(node, gd->fdt_blob, dev_of_offset(dev)) { |
677b5358 BG |
361 | if (fdt_getprop(gd->fdt_blob, node, "gpio-controller", &len)) { |
362 | gpio = node; | |
363 | break; | |
364 | } | |
365 | } | |
366 | ||
367 | if (!gpio) { | |
368 | debug("gpio node not found\n"); | |
369 | return -EINVAL; | |
370 | } | |
371 | ||
372 | addr = parse_address(gpio, "mux", na, ns); | |
373 | if (addr == FDT_ADDR_T_NONE) { | |
2009a8d0 | 374 | debug("mux address not found\n"); |
677b5358 BG |
375 | return -EINVAL; |
376 | } | |
677b5358 | 377 | priv->reg_mux = (void __iomem *)addr; |
2009a8d0 BG |
378 | |
379 | addr = parse_address(gpio, "gpio", na, ns); | |
380 | if (addr == FDT_ADDR_T_NONE) { | |
381 | debug("gpio address not found\n"); | |
382 | return -EINVAL; | |
383 | } | |
384 | priv->reg_gpio = (void __iomem *)addr; | |
c4c726c2 JB |
385 | |
386 | addr = parse_address(gpio, "pull", na, ns); | |
f91121c7 JB |
387 | /* Use gpio region if pull one is not present */ |
388 | if (addr == FDT_ADDR_T_NONE) | |
389 | priv->reg_pull = priv->reg_gpio; | |
390 | else | |
391 | priv->reg_pull = (void __iomem *)addr; | |
c4c726c2 JB |
392 | |
393 | addr = parse_address(gpio, "pull-enable", na, ns); | |
394 | /* Use pull region if pull-enable one is not present */ | |
395 | if (addr == FDT_ADDR_T_NONE) | |
396 | priv->reg_pullen = priv->reg_pull; | |
397 | else | |
398 | priv->reg_pullen = (void __iomem *)addr; | |
399 | ||
f91121c7 JB |
400 | addr = parse_address(gpio, "ds", na, ns); |
401 | /* Drive strength region is optional */ | |
402 | if (addr == FDT_ADDR_T_NONE) | |
403 | priv->reg_ds = NULL; | |
404 | else | |
405 | priv->reg_ds = (void __iomem *)addr; | |
406 | ||
677b5358 BG |
407 | priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev); |
408 | ||
2009a8d0 BG |
409 | /* Lookup GPIO driver */ |
410 | drv = lists_uclass_lookup(UCLASS_GPIO); | |
411 | if (!drv) { | |
412 | puts("Cannot find GPIO driver\n"); | |
413 | return -ENOENT; | |
414 | } | |
415 | ||
416 | name = calloc(1, 32); | |
417 | sprintf(name, "meson-gpio"); | |
418 | ||
419 | /* Create child device UCLASS_GPIO and bind it */ | |
7c9dcfed | 420 | device_bind(dev, priv->data->gpio_driver, name, NULL, gpio, &gpio_dev); |
2009a8d0 BG |
421 | dev_set_of_offset(gpio_dev, gpio); |
422 | ||
677b5358 BG |
423 | return 0; |
424 | } |