fdt_port.c revision 1.3
1/* $NetBSD: fdt_port.c,v 1.3 2019/11/23 18:53:05 jmcneill 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.3 2019/11/23 18:53:05 jmcneill 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; 193device_printf(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; 210device_printf(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 uint64_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_error_dev(self, 302 "%s: missing reg property", 303 fdtbus_get_string(child, "name")); 304 id = i; 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_error_dev(port->port_dp->dp_dev, 353 "%s: missing reg property", 354 fdtbus_get_string(child, "name")); 355 id = i; 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_verbose("%s ", ep_name(ep, buf, sizeof(buf))); 376 aprint_verbose("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