/*-
* Copyright (c) 2018 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ports and endpoints management. from
* linux/Documentation/devicetree/bindings/graph.txt
* Given a device and its node, it enumerates all ports and endpoints for this
* device, and register connections with the remote endpoints.
*/
struct fdt_port {
int port_id;
int port_phandle; /* port's node */
struct fdt_endpoint *port_ep; /* this port's endpoints */
int port_nep; /* number of endpoints for this port */
struct fdt_device_ports *port_dp; /* this port's device */
};
struct fdt_endpoint {
int ep_id;
enum endpoint_type ep_type;
int ep_phandle;
struct fdt_port *ep_port; /* parent of this endpoint */
int ep_rphandle; /* report endpoint */
struct fdt_endpoint *ep_rep;
bool ep_active;
bool ep_enabled;
};
port->port_nep = 0;
for (child = OF_child(phandle); child; child = OF_peer(child)) {
if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
continue;
if (strcmp(buf, "endpoint") != 0)
continue;
port->port_nep++;
}
if (port->port_nep == 0) {
port->port_ep = NULL;
return;
}
port->port_ep =
kmem_zalloc(sizeof(struct fdt_endpoint) * port->port_nep, KM_SLEEP);
KASSERT(port->port_ep != NULL);
/* now scan again ports, looking for endpoints */
for (child = OF_child(phandle), i = 0; child; child = OF_peer(child)) {
if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0)
continue;
if (strcmp(buf, "endpoint") != 0)
continue;
if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) {
if (port->port_nep > 1)
aprint_debug_dev(port->port_dp->dp_dev,
"%s: missing reg property",
fdtbus_get_string(child, "name"));
id = 0;
}
ep = &port->port_ep[i];
ep->ep_id = id;
ep->ep_type = type;
ep->ep_phandle = child;
ep->ep_port = port;
ep->ep_rphandle = fdtbus_get_phandle(child, "remote-endpoint");
ep->ep_rep = fdt_endpoint_get_from_phandle(
port->port_ep[i].ep_rphandle);
rep = ep->ep_rep;
if (rep != NULL && rep->ep_rep != NULL) {
aprint_error("%s: ", ep_name(ep, buf, sizeof(buf)));
aprint_error("remote endpoint %s ",
ep_name(rep, buf, sizeof(buf)));
aprint_error("already connected to %s\n",
ep_name(rep->ep_rep, buf, sizeof(buf)));
} else if (rep != NULL) {
rep->ep_rep = ep;
rep->ep_rphandle = child;
aprint_debug("%s ", ep_name(ep, buf, sizeof(buf)));
aprint_debug("connected to %s\n",
ep_name(rep, buf, sizeof(buf)));
if (rep->ep_type == EP_OTHER)
rep->ep_type = ep->ep_type;
else if (ep->ep_type == EP_OTHER)
ep->ep_type = rep->ep_type;
dp = port->port_dp;
if (dp->dp_ep_connect)
dp->dp_ep_connect(dp->dp_dev, ep, true);
dp = rep->ep_port->port_dp;
if (dp->dp_ep_connect)
dp->dp_ep_connect(dp->dp_dev, rep, true);
}
i++;
}
KASSERT(i == port->port_nep);
}
static const char *
ep_name(struct fdt_endpoint *ep, char *buf, int size)
{
int a;
a = snprintf(&buf[0], size, "%s",
device_xname(ep->ep_port->port_dp->dp_dev));
if (ep->ep_port->port_id >= 0 && a < size)
a += snprintf(&buf[a], size - a, " port %d",
ep->ep_port->port_id);
if (ep->ep_id >= 0 && a < size)
snprintf(&buf[a], size - a, " endpoint %d", ep->ep_id);
return buf;
}