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