xref: /netbsd/sys/dev/fdt/fdt_port.c (revision 61903849)
1 /*	$NetBSD: fdt_port.c,v 1.7 2022/01/21 21:00:26 macallan Exp $	*/
2 
3 /*-
4  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Manuel Bouyer.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * ports and endpoints management. from
34  * linux/Documentation/devicetree/bindings/graph.txt
35  * Given a device and its node, it enumerates all ports and endpoints for this
36  * device, and register connections with the remote endpoints.
37  */
38 
39 #include <sys/cdefs.h>
40 
41 __KERNEL_RCSID(1, "$NetBSD: fdt_port.c,v 1.7 2022/01/21 21:00:26 macallan Exp $");
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/device.h>
46 #include <sys/bus.h>
47 #include <sys/kmem.h>
48 
49 #include <dev/fdt/fdtvar.h>
50 #include <dev/fdt/fdt_port.h>
51 
52 struct fdt_endpoint;
53 
54 struct fdt_port {
55 	int	port_id;
56 	int	port_phandle; /* port's node */
57 	struct fdt_endpoint *port_ep; /* this port's endpoints */
58 	int	port_nep; /* number of endpoints for this port */
59 	struct fdt_device_ports *port_dp; /* this port's device */
60 };
61 
62 struct fdt_endpoint {
63 	int		ep_id;
64 	enum endpoint_type ep_type;
65 	int		ep_phandle;
66 	struct fdt_port	*ep_port; /* parent of this endpoint */
67 	int		ep_rphandle; /* report endpoint */
68 	struct fdt_endpoint *ep_rep;
69 	bool		ep_active;
70 	bool		ep_enabled;
71 };
72 
73 SLIST_HEAD(, fdt_device_ports) fdt_port_devices =
74     SLIST_HEAD_INITIALIZER(&fdt_port_devices);
75 
76 static void fdt_endpoints_register(int, struct fdt_port *, enum endpoint_type);
77 static const char *ep_name(struct fdt_endpoint *, char *, int);
78 
79 struct fdt_endpoint *
fdt_endpoint_get_from_phandle(int rphandle)80 fdt_endpoint_get_from_phandle(int rphandle)
81 {
82 	struct fdt_device_ports *ports;
83 	int p, e;
84 
85 	if (rphandle < 0)
86 		return NULL;
87 
88 	SLIST_FOREACH(ports, &fdt_port_devices, dp_list) {
89 		for (p = 0; p < ports->dp_nports; p++) {
90 			struct fdt_port *port = &ports->dp_port[p];
91 			for (e = 0; e < port->port_nep; e++) {
92 				struct fdt_endpoint *ep = &port->port_ep[e];
93 				if (ep->ep_phandle == rphandle)
94 					return ep;
95 			}
96 		}
97 	}
98 	return NULL;
99 
100 }
101 
102 struct fdt_endpoint *
fdt_endpoint_get_from_index(struct fdt_device_ports * device_ports,int port_index,int ep_index)103 fdt_endpoint_get_from_index(struct fdt_device_ports *device_ports,
104     int port_index, int ep_index)
105 {
106 	int p, e;
107 	for (p = 0; p < device_ports->dp_nports; p++) {
108 		struct fdt_port *port = &device_ports->dp_port[p];
109 		if (port->port_id != port_index)
110 			continue;
111 		for (e = 0; e < port->port_nep; e++) {
112 			struct fdt_endpoint *ep = &port->port_ep[e];
113 			if (ep->ep_id == ep_index) {
114 				return ep;
115 			}
116 		}
117 	}
118 	return NULL;
119 }
120 
121 struct fdt_endpoint *
fdt_endpoint_remote_from_index(struct fdt_device_ports * device_ports,int port_index,int ep_index)122 fdt_endpoint_remote_from_index(struct fdt_device_ports *device_ports,
123     int port_index, int ep_index)
124 {
125 	struct fdt_endpoint *ep;
126 
127 	ep = fdt_endpoint_get_from_index(device_ports, port_index,
128 	    ep_index);
129 	if (ep == NULL)
130 		return NULL;
131 
132 	return fdt_endpoint_remote(ep);
133 }
134 
135 struct fdt_endpoint *
fdt_endpoint_remote(struct fdt_endpoint * ep)136 fdt_endpoint_remote(struct fdt_endpoint *ep)
137 {
138 	return ep->ep_rep;
139 }
140 
141 int
fdt_endpoint_port_index(struct fdt_endpoint * ep)142 fdt_endpoint_port_index(struct fdt_endpoint *ep)
143 {
144 	return ep->ep_port->port_id;
145 }
146 
147 int
fdt_endpoint_index(struct fdt_endpoint * ep)148 fdt_endpoint_index(struct fdt_endpoint *ep)
149 {
150 	return ep->ep_id;
151 }
152 
153 device_t
fdt_endpoint_device(struct fdt_endpoint * ep)154 fdt_endpoint_device(struct fdt_endpoint *ep)
155 {
156 	return ep->ep_port->port_dp->dp_dev;
157 }
158 
159 bool
fdt_endpoint_is_active(struct fdt_endpoint * ep)160 fdt_endpoint_is_active(struct fdt_endpoint *ep)
161 {
162 	return ep->ep_active;
163 }
164 
165 bool
fdt_endpoint_is_enabled(struct fdt_endpoint * ep)166 fdt_endpoint_is_enabled(struct fdt_endpoint *ep)
167 {
168 	return ep->ep_enabled;
169 }
170 
171 enum endpoint_type
fdt_endpoint_type(struct fdt_endpoint * ep)172 fdt_endpoint_type(struct fdt_endpoint *ep)
173 {
174 	return ep->ep_type;
175 }
176 
177 int
fdt_endpoint_activate(struct fdt_endpoint * ep,bool activate)178 fdt_endpoint_activate(struct fdt_endpoint *ep, bool activate)
179 {
180 	struct fdt_endpoint *rep = fdt_endpoint_remote(ep);
181 	struct fdt_device_ports *rdp;
182 	int error = 0;
183 
184 	if (rep == NULL)
185 		return ENODEV;
186 
187 	KASSERT(ep->ep_active == rep->ep_active);
188 	KASSERT(ep->ep_enabled == rep->ep_enabled);
189 	if (!activate && ep->ep_enabled)
190 		return EBUSY;
191 
192 	rdp = rep->ep_port->port_dp;
193 	aprint_debug_dev(rdp->dp_dev, "activating port %d endpoint %d\n",
194 	    fdt_endpoint_port_index(rep), fdt_endpoint_index(rep));
195 	if (rdp->dp_ep_activate)
196 		error = rdp->dp_ep_activate(rdp->dp_dev, rep, activate);
197 
198 	if (error == 0)
199 		rep->ep_active = ep->ep_active = activate;
200 	return error;
201 }
202 
203 int
fdt_endpoint_activate_direct(struct fdt_endpoint * ep,bool activate)204 fdt_endpoint_activate_direct(struct fdt_endpoint *ep, bool activate)
205 {
206 	struct fdt_device_ports *dp;
207 	int error = 0;
208 
209 	dp = ep->ep_port->port_dp;
210 	aprint_debug_dev(dp->dp_dev, "activating port %d endpoint %d (direct)\n",
211 	    fdt_endpoint_port_index(ep), fdt_endpoint_index(ep));
212 	if (dp->dp_ep_activate)
213 		error = dp->dp_ep_activate(dp->dp_dev, ep, activate);
214 
215 	return error;
216 }
217 
218 int
fdt_endpoint_enable(struct fdt_endpoint * ep,bool enable)219 fdt_endpoint_enable(struct fdt_endpoint *ep, bool enable)
220 {
221 	struct fdt_endpoint *rep = fdt_endpoint_remote(ep);
222 	struct fdt_device_ports *rdp;
223 	int error = 0;
224 
225 	if (rep == NULL)
226 		return EINVAL;
227 
228 	KASSERT(ep->ep_active == rep->ep_active);
229 	KASSERT(ep->ep_enabled == rep->ep_enabled);
230 	if (ep->ep_active == false)
231 		return EINVAL;
232 
233 	rdp = rep->ep_port->port_dp;
234 	if (rdp->dp_ep_enable)
235 		error = rdp->dp_ep_enable(rdp->dp_dev, rep, enable);
236 
237 	if (error == 0)
238 		rep->ep_enabled = ep->ep_enabled = enable;
239 	return error;
240 }
241 
242 void *
fdt_endpoint_get_data(struct fdt_endpoint * ep)243 fdt_endpoint_get_data(struct fdt_endpoint *ep)
244 {
245 	struct fdt_device_ports *dp = ep->ep_port->port_dp;
246 
247 	if (dp->dp_ep_get_data)
248 		return dp->dp_ep_get_data(dp->dp_dev, ep);
249 
250 	return NULL;
251 }
252 
253 int
fdt_ports_register(struct fdt_device_ports * ports,device_t self,int phandle,enum endpoint_type type)254 fdt_ports_register(struct fdt_device_ports *ports, device_t self,
255     int phandle, enum endpoint_type type)
256 {
257 	int port_phandle, child;
258 	int i;
259 	char buf[20];
260 	bus_addr_t id;
261 
262 	ports->dp_dev = self;
263 	SLIST_INSERT_HEAD(&fdt_port_devices, ports, dp_list);
264 
265 	/*
266 	 * walk the childs looking for ports. ports may be grouped under
267 	 * an optional ports node
268 	 */
269 	port_phandle = phandle;
270 again:
271 	ports->dp_nports = 0;
272 	for (child = OF_child(port_phandle); child; child = OF_peer(child)) {
273 		if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
274 			continue;
275 		if (strcmp(buf, "ports") == 0) {
276 			port_phandle = child;
277 			goto again;
278 		}
279 		if (strcmp(buf, "port") != 0)
280 			continue;
281 		ports->dp_nports++;
282 	}
283 	if (ports->dp_nports == 0)
284 		return 0;
285 
286 	ports->dp_port =
287 	    kmem_zalloc(sizeof(struct fdt_port) * ports->dp_nports, KM_SLEEP);
288 	KASSERT(ports->dp_port != NULL);
289 	/* now scan again ports, looking for endpoints */
290 	for (child = OF_child(port_phandle), i = 0; child;
291 	    child = OF_peer(child)) {
292 		if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
293 			continue;
294 		if (strcmp(buf, "ports") == 0) {
295 			panic("fdt_ports_register: undetected ports");
296 		}
297 		if (strcmp(buf, "port") != 0)
298 			continue;
299 		if (fdtbus_get_reg(child, 0, &id, NULL) != 0) {
300 			if (ports->dp_nports > 1)
301 				aprint_debug_dev(self,
302 				    "%s: missing reg property",
303 				    fdtbus_get_string(child, "name"));
304 			id = 0;
305 		}
306 		ports->dp_port[i].port_id = id;
307 		ports->dp_port[i].port_phandle = child;
308 		ports->dp_port[i].port_dp = ports;
309 		fdt_endpoints_register(child, &ports->dp_port[i], type);
310 		i++;
311 	}
312 	KASSERT(i == ports->dp_nports);
313 	return 0;
314 }
315 
316 
317 static void
fdt_endpoints_register(int phandle,struct fdt_port * port,enum endpoint_type type)318 fdt_endpoints_register(int phandle, struct fdt_port *port,
319     enum endpoint_type type)
320 {
321 	int child;
322 	int i;
323 	char buf[128];
324 	uint64_t id;
325 	struct fdt_endpoint *ep, *rep;
326 	struct fdt_device_ports *dp;
327 
328 	port->port_nep = 0;
329 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
330 		if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
331 			continue;
332 		if (strcmp(buf, "endpoint") != 0)
333 			continue;
334 		port->port_nep++;
335 	}
336 	if (port->port_nep == 0) {
337 		port->port_ep = NULL;
338 		return;
339 	}
340 
341 	port->port_ep =
342 	    kmem_zalloc(sizeof(struct fdt_endpoint) * port->port_nep, KM_SLEEP);
343 	KASSERT(port->port_ep != NULL);
344 	/* now scan again ports, looking for endpoints */
345 	for (child = OF_child(phandle), i = 0; child; child = OF_peer(child)) {
346 		if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
347 			continue;
348 		if (strcmp(buf, "endpoint") != 0)
349 			continue;
350 		if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) {
351 			if (port->port_nep > 1)
352 				aprint_debug_dev(port->port_dp->dp_dev,
353 				    "%s: missing reg property",
354 				    fdtbus_get_string(child, "name"));
355 			id = 0;
356 		}
357 		ep = &port->port_ep[i];
358 		ep->ep_id = id;
359 		ep->ep_type = type;
360 		ep->ep_phandle = child;
361 		ep->ep_port = port;
362 		ep->ep_rphandle = fdtbus_get_phandle(child, "remote-endpoint");
363 		ep->ep_rep = fdt_endpoint_get_from_phandle(
364 		    port->port_ep[i].ep_rphandle);
365 		rep = ep->ep_rep;
366 		if (rep != NULL && rep->ep_rep != NULL) {
367 			aprint_error("%s: ", ep_name(ep, buf, sizeof(buf)));
368 			aprint_error("remote endpoint %s ",
369 			    ep_name(rep, buf, sizeof(buf)));
370 			aprint_error("already connected to %s\n",
371 			    ep_name(rep->ep_rep, buf, sizeof(buf)));
372 		} else if (rep != NULL) {
373 			rep->ep_rep = ep;
374 			rep->ep_rphandle = child;
375 			aprint_debug("%s ", ep_name(ep, buf, sizeof(buf)));
376 			aprint_debug("connected to %s\n",
377 			    ep_name(rep, buf, sizeof(buf)));
378 			if (rep->ep_type == EP_OTHER)
379 				rep->ep_type = ep->ep_type;
380 			else if (ep->ep_type == EP_OTHER)
381 				ep->ep_type = rep->ep_type;
382 			dp = port->port_dp;
383 			if (dp->dp_ep_connect)
384 				dp->dp_ep_connect(dp->dp_dev, ep, true);
385 			dp = rep->ep_port->port_dp;
386 			if (dp->dp_ep_connect)
387 				dp->dp_ep_connect(dp->dp_dev, rep, true);
388 		}
389 		i++;
390 	}
391 	KASSERT(i == port->port_nep);
392 }
393 
394 static const char *
ep_name(struct fdt_endpoint * ep,char * buf,int size)395 ep_name(struct fdt_endpoint *ep, char *buf, int size)
396 {
397 	int a;
398 
399 	a = snprintf(&buf[0], size, "%s",
400 	    device_xname(ep->ep_port->port_dp->dp_dev));
401 	if (ep->ep_port->port_id >= 0 && a < size)
402 		a += snprintf(&buf[a], size - a, " port %d",
403 		    ep->ep_port->port_id);
404 	if (ep->ep_id >= 0 && a < size)
405 		snprintf(&buf[a], size - a, " endpoint %d", ep->ep_id);
406 	return buf;
407 }
408