1/*	$OpenBSD: ofw_gpio.c,v 1.3 2019/08/26 09:22:27 kettenis Exp $	*/
2/*
3 * Copyright (c) 2016, 2019 Mark Kettenis
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <sys/systm.h>
20#include <sys/malloc.h>
21
22#include <dev/ofw/openfirm.h>
23#include <dev/ofw/ofw_gpio.h>
24
25LIST_HEAD(, gpio_controller) gpio_controllers =
26	LIST_HEAD_INITIALIZER(gpio_controllers);
27
28void
29gpio_controller_register(struct gpio_controller *gc)
30{
31	int child;
32
33	gc->gc_cells = OF_getpropint(gc->gc_node, "#gpio-cells", 2);
34	gc->gc_phandle = OF_getpropint(gc->gc_node, "phandle", 0);
35	if (gc->gc_phandle == 0)
36		return;
37
38	LIST_INSERT_HEAD(&gpio_controllers, gc, gc_list);
39
40	/* Process GPIO hogs. */
41	for (child = OF_child(gc->gc_node); child; child = OF_peer(child)) {
42		uint32_t *gpios;
43		uint32_t *gpio;
44		int len, config, active;
45
46		if (OF_getproplen(child, "gpio-hog") != 0)
47			continue;
48
49		len = OF_getproplen(child, "gpios");
50		if (len <= 0)
51			continue;
52
53		/*
54		 * These need to be processed in the order prescribed
55		 * by the device tree binding.  First match wins.
56		 */
57		if (OF_getproplen(child, "input") == 0) {
58			config = GPIO_CONFIG_INPUT;
59			active = 0;
60		} else if (OF_getproplen(child, "output-low") == 0) {
61			config = GPIO_CONFIG_OUTPUT;
62			active = 0;
63		} else if (OF_getproplen(child, "output-high") == 0) {
64			config = GPIO_CONFIG_OUTPUT;
65			active = 1;
66		} else
67			continue;
68
69		gpios = malloc(len, M_TEMP, M_WAITOK);
70		OF_getpropintarray(child, "gpios", gpios, len);
71
72		gpio = gpios;
73		while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
74			gc->gc_config_pin(gc->gc_cookie, gpio, config);
75			if (config & GPIO_CONFIG_OUTPUT)
76				gc->gc_set_pin(gc->gc_cookie, gpio, active);
77			gpio += gc->gc_cells;
78		}
79
80		free(gpios, M_TEMP, len);
81	}
82}
83
84void
85gpio_controller_config_pin(uint32_t *cells, int config)
86{
87	struct gpio_controller *gc;
88	uint32_t phandle = cells[0];
89
90	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
91		if (gc->gc_phandle == phandle)
92			break;
93	}
94
95	if (gc && gc->gc_config_pin)
96		gc->gc_config_pin(gc->gc_cookie, &cells[1], config);
97}
98
99int
100gpio_controller_get_pin(uint32_t *cells)
101{
102	struct gpio_controller *gc;
103	uint32_t phandle = cells[0];
104	int val = 0;
105
106	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
107		if (gc->gc_phandle == phandle)
108			break;
109	}
110
111	if (gc && gc->gc_get_pin)
112		val = gc->gc_get_pin(gc->gc_cookie, &cells[1]);
113
114	return val;
115}
116
117void
118gpio_controller_set_pin(uint32_t *cells, int val)
119{
120	struct gpio_controller *gc;
121	uint32_t phandle = cells[0];
122
123	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
124		if (gc->gc_phandle == phandle)
125			break;
126	}
127
128	if (gc && gc->gc_set_pin)
129		gc->gc_set_pin(gc->gc_cookie, &cells[1], val);
130}
131
132uint32_t *
133gpio_controller_next_pin(uint32_t *cells)
134{
135	struct gpio_controller *gc;
136	uint32_t phandle = cells[0];
137
138	LIST_FOREACH(gc, &gpio_controllers, gc_list)
139		if (gc->gc_phandle == phandle)
140			return cells + gc->gc_cells + 1;
141
142	return NULL;
143}
144