1 /*-
2 * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/gpio.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/sx.h>
37
38 #include <machine/bus.h>
39
40 #include <dev/fdt/fdt_common.h>
41 #include <dev/gpio/gpiobusvar.h>
42
43 #include "as3722.h"
44
45 MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
46
47 /* AS3722_GPIOx_CONTROL MODE and IOSF definition. */
48 #define AS3722_IOSF_GPIO 0x00
49 #define AS3722_IOSF_INTERRUPT_OUT 0x01
50 #define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02
51 #define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03
52 #define AS3722_IOSF_PWM_IN 0x04
53 #define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05
54 #define AS3722_IOSF_OC_PG_SD0 0x06
55 #define AS3722_IOSF_POWERGOOD_OUT 0x07
56 #define AS3722_IOSF_CLK32K_OUT 0x08
57 #define AS3722_IOSF_WATCHDOG_IN 0x09
58 #define AS3722_IOSF_SOFT_RESET_IN 0x0b
59 #define AS3722_IOSF_PWM_OUT 0x0c
60 #define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d
61 #define AS3722_IOSF_OC_PG_SD6 0x0e
62
63 #define AS3722_MODE_INPUT 0
64 #define AS3722_MODE_PUSH_PULL 1
65 #define AS3722_MODE_OPEN_DRAIN 2
66 #define AS3722_MODE_TRISTATE 3
67 #define AS3722_MODE_INPUT_PULL_UP_LV 4
68 #define AS3722_MODE_INPUT_PULL_DOWN 5
69 #define AS3722_MODE_OPEN_DRAIN_LV 6
70 #define AS3722_MODE_PUSH_PULL_LV 7
71
72 #define NGPIO 8
73
74 #define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock)
75 #define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock)
76 #define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
77
78 #define AS3722_CFG_BIAS_DISABLE 0x0001
79 #define AS3722_CFG_BIAS_PULL_UP 0x0002
80 #define AS3722_CFG_BIAS_PULL_DOWN 0x0004
81 #define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008
82 #define AS3722_CFG_OPEN_DRAIN 0x0010
83
84 static const struct {
85 const char *name;
86 int config; /* AS3722_CFG_ */
87 } as3722_cfg_names[] = {
88 {"bias-disable", AS3722_CFG_BIAS_DISABLE},
89 {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP},
90 {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN},
91 {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE},
92 {"drive-open-drain", AS3722_CFG_OPEN_DRAIN},
93 };
94
95 static struct {
96 const char *name;
97 int fnc_val;
98 } as3722_fnc_table[] = {
99 {"gpio", AS3722_IOSF_GPIO},
100 {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT},
101 {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
102 {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT},
103 {"pwm-in", AS3722_IOSF_PWM_IN},
104 {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY},
105 {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0},
106 {"powergood-out", AS3722_IOSF_POWERGOOD_OUT},
107 {"clk32k-out", AS3722_IOSF_CLK32K_OUT},
108 {"watchdog-in", AS3722_IOSF_WATCHDOG_IN},
109 {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN},
110 {"pwm-out", AS3722_IOSF_PWM_OUT},
111 {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
112 {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6},
113 };
114
115 struct as3722_pincfg {
116 char *function;
117 int flags;
118 };
119
120 struct as3722_gpio_pin {
121 int pin_caps;
122 uint8_t pin_ctrl_reg;
123 char pin_name[GPIOMAXNAME];
124 int pin_cfg_flags;
125 };
126
127 /* --------------------------------------------------------------------------
128 *
129 * Pinmux functions.
130 */
131 static int
132 as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
133 {
134 int i;
135
136 for (i = 0; i < nitems(as3722_fnc_table); i++) {
137 if (strcmp(as3722_fnc_table[i].name, name) == 0)
138 return (as3722_fnc_table[i].fnc_val);
139 }
140 return (-1);
141 }
142
143 static int
144 as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
145 struct as3722_pincfg *cfg)
146 {
147 uint8_t ctrl;
148 int rv, fnc, pin;
149
150 for (pin = 0; pin < sc->gpio_npins; pin++) {
151 if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
152 break;
153 }
154 if (pin >= sc->gpio_npins) {
155 device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
156 return (ENXIO);
157 }
158
159 ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
160 sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
161 if (cfg->function != NULL) {
162 fnc = as3722_pinmux_get_function(sc, cfg->function);
163 if (fnc == -1) {
164 device_printf(sc->dev,
165 "Unknown function %s for pin %s\n", cfg->function,
166 sc->gpio_pins[pin]->pin_name);
167 return (ENXIO);
168 }
169 switch (fnc) {
170 case AS3722_IOSF_INTERRUPT_OUT:
171 case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
172 case AS3722_IOSF_OC_PG_SD0:
173 case AS3722_IOSF_POWERGOOD_OUT:
174 case AS3722_IOSF_CLK32K_OUT:
175 case AS3722_IOSF_PWM_OUT:
176 case AS3722_IOSF_OC_PG_SD6:
177 ctrl &= ~(AS3722_GPIO_MODE_MASK <<
178 AS3722_GPIO_MODE_SHIFT);
179 ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
180 /* XXX Handle flags (OC + pullup) */
181 break;
182 case AS3722_IOSF_GPIO_IN_INTERRUPT:
183 case AS3722_IOSF_PWM_IN:
184 case AS3722_IOSF_VOLTAGE_IN_STANDBY:
185 case AS3722_IOSF_WATCHDOG_IN:
186 case AS3722_IOSF_SOFT_RESET_IN:
187 ctrl &= ~(AS3722_GPIO_MODE_MASK <<
188 AS3722_GPIO_MODE_SHIFT);
189 ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
190 /* XXX Handle flags (pulldown + pullup) */
191
192 default:
193 break;
194 }
195 ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
196 ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
197 }
198 rv = 0;
199 if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
200 rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
201 sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
202 }
203 return (rv);
204 }
205
206 static int
207 as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
208 struct as3722_pincfg *cfg, char **pins, int *lpins)
209 {
210 int rv, i;
211
212 *lpins = OF_getprop_alloc(node, "pins", (void **)pins);
213 if (*lpins <= 0)
214 return (ENOENT);
215
216 /* Read function (mux) settings. */
217 rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
218 if (rv <= 0)
219 cfg->function = NULL;
220
221 /* Read boolean properties. */
222 for (i = 0; i < nitems(as3722_cfg_names); i++) {
223 if (OF_hasprop(node, as3722_cfg_names[i].name))
224 cfg->flags |= as3722_cfg_names[i].config;
225 }
226 return (0);
227 }
228
229 static int
230 as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
231 {
232 struct as3722_pincfg cfg;
233 char *pins, *pname;
234 int i, len, lpins, rv;
235
236 rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
237 if (rv != 0)
238 return (rv);
239
240 len = 0;
241 pname = pins;
242 do {
243 i = strlen(pname) + 1;
244 rv = as3722_pinmux_config_node(sc, pname, &cfg);
245 if (rv != 0) {
246 device_printf(sc->dev,
247 "Cannot configure pin: %s: %d\n", pname, rv);
248 }
249 len += i;
250 pname += i;
251 } while (len < lpins);
252
253 if (pins != NULL)
254 OF_prop_free(pins);
255 if (cfg.function != NULL)
256 OF_prop_free(cfg.function);
257
258 return (rv);
259 }
260
261 int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
262 {
263 struct as3722_softc *sc;
264 phandle_t node, cfgnode;
265 int rv;
266
267 sc = device_get_softc(dev);
268 cfgnode = OF_node_from_xref(cfgxref);
269
270 for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
271 if (!ofw_bus_node_status_okay(node))
272 continue;
273 rv = as3722_pinmux_process_node(sc, node);
274 if (rv != 0)
275 device_printf(dev, "Failed to process pinmux");
276 }
277 return (0);
278 }
279
280 /* --------------------------------------------------------------------------
281 *
282 * GPIO
283 */
284 device_t
285 as3722_gpio_get_bus(device_t dev)
286 {
287 struct as3722_softc *sc;
288
289 sc = device_get_softc(dev);
290 return (sc->gpio_busdev);
291 }
292
293 int
294 as3722_gpio_pin_max(device_t dev, int *maxpin)
295 {
296
297 *maxpin = NGPIO - 1;
298 return (0);
299 }
300
301 int
302 as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
303 {
304 struct as3722_softc *sc;
305
306 sc = device_get_softc(dev);
307 if (pin >= sc->gpio_npins)
308 return (EINVAL);
309 GPIO_LOCK(sc);
310 *caps = sc->gpio_pins[pin]->pin_caps;
311 GPIO_UNLOCK(sc);
312 return (0);
313 }
314
315 int
316 as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
317 {
318 struct as3722_softc *sc;
319
320 sc = device_get_softc(dev);
321 if (pin >= sc->gpio_npins)
322 return (EINVAL);
323 GPIO_LOCK(sc);
324 memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
325 GPIO_UNLOCK(sc);
326 return (0);
327 }
328
329 int
330 as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
331 {
332 struct as3722_softc *sc;
333 uint8_t tmp, mode, iosf;
334 uint32_t flags;
335 bool inverted;
336
337 sc = device_get_softc(dev);
338 if (pin >= sc->gpio_npins)
339 return (EINVAL);
340
341 GPIO_LOCK(sc);
342 tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
343 GPIO_UNLOCK(sc);
344 iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
345 mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
346 inverted = (tmp & AS3722_GPIO_INVERT) != 0;
347 /* Is pin in GPIO mode ? */
348 if (iosf != AS3722_IOSF_GPIO)
349 return (ENXIO);
350
351 flags = 0;
352 switch (mode) {
353 case AS3722_MODE_INPUT:
354 flags = GPIO_PIN_INPUT;
355 break;
356 case AS3722_MODE_PUSH_PULL:
357 case AS3722_MODE_PUSH_PULL_LV:
358 flags = GPIO_PIN_OUTPUT;
359 break;
360 case AS3722_MODE_OPEN_DRAIN:
361 case AS3722_MODE_OPEN_DRAIN_LV:
362 flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
363 break;
364 case AS3722_MODE_TRISTATE:
365 flags = GPIO_PIN_TRISTATE;
366 break;
367 case AS3722_MODE_INPUT_PULL_UP_LV:
368 flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
369 break;
370
371 case AS3722_MODE_INPUT_PULL_DOWN:
372 flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
373 break;
374 }
375 if (inverted)
376 flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
377 *out_flags = flags;
378 return (0);
379 }
380
381 static int
382 as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
383 {
384 int flags;
385
386 flags = sc->gpio_pins[pin]->pin_cfg_flags;
387
388 /* Tristate mode. */
389 if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
390 gpio_flags & GPIO_PIN_TRISTATE)
391 return (AS3722_MODE_TRISTATE);
392
393 /* Open drain modes. */
394 if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
395 /* Only pull up have effect */
396 if (flags & AS3722_CFG_BIAS_PULL_UP ||
397 gpio_flags & GPIO_PIN_PULLUP)
398 return (AS3722_MODE_OPEN_DRAIN_LV);
399 return (AS3722_MODE_OPEN_DRAIN);
400 }
401 /* Input modes. */
402 if (gpio_flags & GPIO_PIN_INPUT) {
403 /* Accept pull up or pull down. */
404 if (flags & AS3722_CFG_BIAS_PULL_UP ||
405 gpio_flags & GPIO_PIN_PULLUP)
406 return (AS3722_MODE_INPUT_PULL_UP_LV);
407
408 if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
409 gpio_flags & GPIO_PIN_PULLDOWN)
410 return (AS3722_MODE_INPUT_PULL_DOWN);
411 return (AS3722_MODE_INPUT);
412 }
413 /*
414 * Output modes.
415 * Pull down is used as indicator of low voltage output.
416 */
417 if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
418 gpio_flags & GPIO_PIN_PULLDOWN)
419 return (AS3722_MODE_PUSH_PULL_LV);
420 return (AS3722_MODE_PUSH_PULL);
421 }
422
423 int
424 as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
425 {
426 struct as3722_softc *sc;
427 uint8_t ctrl, mode, iosf;
428 int rv;
429
430 sc = device_get_softc(dev);
431 if (pin >= sc->gpio_npins)
432 return (EINVAL);
433
434 GPIO_LOCK(sc);
435 ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
436 iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
437 /* Is pin in GPIO mode ? */
438 if (iosf != AS3722_IOSF_GPIO) {
439 GPIO_UNLOCK(sc);
440 return (ENXIO);
441 }
442 mode = as3722_gpio_get_mode(sc, pin, flags);
443 ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
444 ctrl |= mode << AS3722_GPIO_MODE_SHIFT;
445 rv = 0;
446 if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
447 rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
448 sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
449 }
450 GPIO_UNLOCK(sc);
451 return (rv);
452 }
453
454 int
455 as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
456 {
457 struct as3722_softc *sc;
458 uint8_t tmp;
459 int rv;
460
461 sc = device_get_softc(dev);
462 if (pin >= sc->gpio_npins)
463 return (EINVAL);
464
465 tmp = (val != 0) ? 1 : 0;
466 if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
467 tmp ^= 1;
468
469 GPIO_LOCK(sc);
470 rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
471 GPIO_UNLOCK(sc);
472 return (rv);
473 }
474
475 int
476 as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
477 {
478 struct as3722_softc *sc;
479 uint8_t tmp, mode, ctrl;
480 int rv;
481
482 sc = device_get_softc(dev);
483 if (pin >= sc->gpio_npins)
484 return (EINVAL);
485
486 GPIO_LOCK(sc);
487 ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
488 mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
489 if ((mode == AS3722_MODE_PUSH_PULL) ||
490 (mode == AS3722_MODE_PUSH_PULL_LV))
491 rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
492 else
493 rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
494 GPIO_UNLOCK(sc);
495 if (rv != 0)
496 return (rv);
497
498 *val = tmp & (1 << pin) ? 1 : 0;
499 if (ctrl & AS3722_GPIO_INVERT)
500 *val ^= 1;
501 return (0);
502 }
503
504 int
505 as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
506 {
507 struct as3722_softc *sc;
508 uint8_t tmp;
509 int rv;
510
511 sc = device_get_softc(dev);
512 if (pin >= sc->gpio_npins)
513 return (EINVAL);
514
515 GPIO_LOCK(sc);
516 rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
517 if (rv != 0) {
518 GPIO_UNLOCK(sc);
519 return (rv);
520 }
521 tmp ^= (1 <<pin);
522 rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp);
523 GPIO_UNLOCK(sc);
524 return (0);
525 }
526
527 int
528 as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
529 int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
530 {
531
532 if (gcells != 2)
533 return (ERANGE);
534 *pin = gpios[0];
535 *flags= gpios[1];
536 return (0);
537 }
538
539 int
540 as3722_gpio_attach(struct as3722_softc *sc, phandle_t node)
541 {
542 struct as3722_gpio_pin *pin;
543 int i, rv;
544
545 sx_init(&sc->gpio_lock, "AS3722 GPIO lock");
546 sc->gpio_npins = NGPIO;
547 sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
548 sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
549
550 sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
551 if (sc->gpio_busdev == NULL)
552 return (ENXIO);
553 for (i = 0; i < sc->gpio_npins; i++) {
554 sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
555 M_AS3722_GPIO, M_WAITOK | M_ZERO);
556 pin = sc->gpio_pins[i];
557 sprintf(pin->pin_name, "gpio%d", i);
558 pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
559 GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
560 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
561 GPIO_PIN_INVOUT;
562 rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
563 if (rv != 0) {
564 device_printf(sc->dev,
565 "Cannot read configuration for pin %s\n",
566 sc->gpio_pins[i]->pin_name);
567 }
568 }
569 return (0);
570 }
Cache object: 487846aa2523c156934e6e4df3fb087e
|