xref: /openbsd-src/sys/dev/ofw/ofw_gpio.c (revision a436d6bbe515c896c93526df23a13a15df4158f3)
1 /*	$OpenBSD: ofw_gpio.c,v 1.4 2025/01/09 19:38:13 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 <machine/fdt.h>
23 
24 #include <dev/ofw/openfirm.h>
25 #include <dev/ofw/ofw_gpio.h>
26 
27 LIST_HEAD(, gpio_controller) gpio_controllers =
28 	LIST_HEAD_INITIALIZER(gpio_controllers);
29 
30 void
31 gpio_controller_register(struct gpio_controller *gc)
32 {
33 	int child;
34 
35 	gc->gc_cells = OF_getpropint(gc->gc_node, "#gpio-cells", 2);
36 	gc->gc_phandle = OF_getpropint(gc->gc_node, "phandle", 0);
37 	if (gc->gc_phandle == 0)
38 		return;
39 
40 	LIST_INSERT_HEAD(&gpio_controllers, gc, gc_list);
41 
42 	/* Process GPIO hogs. */
43 	for (child = OF_child(gc->gc_node); child; child = OF_peer(child)) {
44 		uint32_t *gpios;
45 		uint32_t *gpio;
46 		int len, config, active;
47 
48 		if (OF_getproplen(child, "gpio-hog") != 0)
49 			continue;
50 
51 		len = OF_getproplen(child, "gpios");
52 		if (len <= 0)
53 			continue;
54 
55 		/*
56 		 * These need to be processed in the order prescribed
57 		 * by the device tree binding.  First match wins.
58 		 */
59 		if (OF_getproplen(child, "input") == 0) {
60 			config = GPIO_CONFIG_INPUT;
61 			active = 0;
62 		} else if (OF_getproplen(child, "output-low") == 0) {
63 			config = GPIO_CONFIG_OUTPUT;
64 			active = 0;
65 		} else if (OF_getproplen(child, "output-high") == 0) {
66 			config = GPIO_CONFIG_OUTPUT;
67 			active = 1;
68 		} else
69 			continue;
70 
71 		gpios = malloc(len, M_TEMP, M_WAITOK);
72 		OF_getpropintarray(child, "gpios", gpios, len);
73 
74 		gpio = gpios;
75 		while (gpio && gpio < gpios + (len / sizeof(uint32_t))) {
76 			gc->gc_config_pin(gc->gc_cookie, gpio, config);
77 			if (config & GPIO_CONFIG_OUTPUT)
78 				gc->gc_set_pin(gc->gc_cookie, gpio, active);
79 			gpio += gc->gc_cells;
80 		}
81 
82 		free(gpios, M_TEMP, len);
83 	}
84 }
85 
86 void
87 gpio_controller_config_pin(uint32_t *cells, int config)
88 {
89 	struct gpio_controller *gc;
90 	uint32_t phandle = cells[0];
91 
92 	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
93 		if (gc->gc_phandle == phandle)
94 			break;
95 	}
96 
97 	if (gc && gc->gc_config_pin)
98 		gc->gc_config_pin(gc->gc_cookie, &cells[1], config);
99 }
100 
101 int
102 gpio_controller_get_pin(uint32_t *cells)
103 {
104 	struct gpio_controller *gc;
105 	uint32_t phandle = cells[0];
106 	int val = 0;
107 
108 	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
109 		if (gc->gc_phandle == phandle)
110 			break;
111 	}
112 
113 	if (gc && gc->gc_get_pin)
114 		val = gc->gc_get_pin(gc->gc_cookie, &cells[1]);
115 
116 	return val;
117 }
118 
119 void
120 gpio_controller_set_pin(uint32_t *cells, int val)
121 {
122 	struct gpio_controller *gc;
123 	uint32_t phandle = cells[0];
124 
125 	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
126 		if (gc->gc_phandle == phandle)
127 			break;
128 	}
129 
130 	if (gc && gc->gc_set_pin)
131 		gc->gc_set_pin(gc->gc_cookie, &cells[1], val);
132 }
133 
134 uint32_t *
135 gpio_controller_next_pin(uint32_t *cells)
136 {
137 	struct gpio_controller *gc;
138 	uint32_t phandle = cells[0];
139 
140 	LIST_FOREACH(gc, &gpio_controllers, gc_list)
141 		if (gc->gc_phandle == phandle)
142 			return cells + gc->gc_cells + 1;
143 
144 	return NULL;
145 }
146 
147 void *
148 gpio_controller_intr_establish(uint32_t *cells, int ipl, struct cpu_info *ci,
149     int (*func)(void *), void *arg, char *name)
150 {
151 	struct gpio_controller *gc;
152 	uint32_t phandle = cells[0];
153 
154 	LIST_FOREACH(gc, &gpio_controllers, gc_list) {
155 		if (gc->gc_phandle == phandle)
156 			break;
157 	}
158 
159 	if (gc && gc->gc_intr_establish) {
160 		return gc->gc_intr_establish(gc->gc_cookie, &cells[1], ipl,
161 		    ci, func, arg, name);
162 	}
163 
164 	return NULL;
165 }
166 
167 void
168 gpio_controller_intr_disestablish(void *ih)
169 {
170 	fdt_intr_disestablish(ih);
171 }
172