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
52struct fdt_endpoint;
53
54struct 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
62struct 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
73SLIST_HEAD(, fdt_device_ports) fdt_port_devices =
74    SLIST_HEAD_INITIALIZER(&fdt_port_devices);
75
76static void fdt_endpoints_register(int, struct fdt_port *, enum endpoint_type);
77static const char *ep_name(struct fdt_endpoint *, char *, int);
78
79struct fdt_endpoint *
80fdt_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
102struct fdt_endpoint *
103fdt_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
121struct fdt_endpoint *
122fdt_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
135struct fdt_endpoint *
136fdt_endpoint_remote(struct fdt_endpoint *ep)
137{
138	return ep->ep_rep;
139}
140
141int
142fdt_endpoint_port_index(struct fdt_endpoint *ep)
143{
144	return ep->ep_port->port_id;
145}
146
147int
148fdt_endpoint_index(struct fdt_endpoint *ep)
149{
150	return ep->ep_id;
151}
152
153device_t
154fdt_endpoint_device(struct fdt_endpoint *ep)
155{
156	return ep->ep_port->port_dp->dp_dev;
157}
158
159bool
160fdt_endpoint_is_active(struct fdt_endpoint *ep)
161{
162	return ep->ep_active;
163}
164
165bool
166fdt_endpoint_is_enabled(struct fdt_endpoint *ep)
167{
168	return ep->ep_enabled;
169}
170
171enum endpoint_type
172fdt_endpoint_type(struct fdt_endpoint *ep)
173{
174	return ep->ep_type;
175}
176
177int
178fdt_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
203int
204fdt_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
218int
219fdt_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
242void *
243fdt_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
253int
254fdt_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;
270again:
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
317static void
318fdt_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
394static const char *
395ep_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