fdt_port.c revision 1.1
1/* $NetBSD: fdt_port.c,v 1.1 2018/04/03 12:40:20 bouyer 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.1 2018/04/03 12:40:20 bouyer 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(struct fdt_endpoint *ep) 123{ 124 return ep->ep_rep; 125} 126 127int 128fdt_endpoint_port_index(struct fdt_endpoint *ep) 129{ 130 return ep->ep_port->port_id; 131} 132 133int 134fdt_endpoint_index(struct fdt_endpoint *ep) 135{ 136 return ep->ep_id; 137} 138 139device_t 140fdt_endpoint_device(struct fdt_endpoint *ep) 141{ 142 return ep->ep_port->port_dp->dp_dev; 143} 144 145bool 146fdt_endpoint_is_active(struct fdt_endpoint *ep) 147{ 148 return ep->ep_active; 149} 150 151bool 152fdt_endpoint_is_enabled(struct fdt_endpoint *ep) 153{ 154 return ep->ep_enabled; 155} 156 157int 158fdt_endpoint_activate(struct fdt_endpoint *ep, bool activate) 159{ 160 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 161 struct fdt_device_ports *rdp; 162 int error = 0; 163 164 if (rep == NULL) 165 return ENODEV; 166 167 KASSERT(ep->ep_active == rep->ep_active); 168 KASSERT(ep->ep_enabled == rep->ep_enabled); 169 if (!activate && ep->ep_enabled) 170 return EBUSY; 171 172 rdp = rep->ep_port->port_dp; 173 if (rdp->dp_ep_activate) 174 error = rdp->dp_ep_activate(rdp->dp_dev, rep, activate); 175 176 if (error == 0) 177 rep->ep_active = ep->ep_active = activate; 178 return error; 179} 180 181int 182fdt_endpoint_enable(struct fdt_endpoint *ep, bool enable) 183{ 184 struct fdt_endpoint *rep = fdt_endpoint_remote(ep); 185 struct fdt_device_ports *rdp; 186 int error = 0; 187 188 if (rep == NULL) 189 return EINVAL; 190 191 KASSERT(ep->ep_active == rep->ep_active); 192 KASSERT(ep->ep_enabled == rep->ep_enabled); 193 if (ep->ep_active == false) 194 return EINVAL; 195 196 rdp = rep->ep_port->port_dp; 197 if (rdp->dp_ep_enable) 198 error = rdp->dp_ep_enable(rdp->dp_dev, rep, enable); 199 200 if (error == 0) 201 rep->ep_enabled = ep->ep_enabled = enable; 202 return error; 203} 204 205void * 206fdt_endpoint_get_data(struct fdt_endpoint *ep) 207{ 208 struct fdt_device_ports *dp = ep->ep_port->port_dp; 209 210 if (dp->dp_ep_get_data) 211 return dp->dp_ep_get_data(dp->dp_dev, ep); 212 213 return NULL; 214} 215 216int 217fdt_ports_register(struct fdt_device_ports *ports, device_t self, 218 int phandle, enum endpoint_type type) 219{ 220 int port_phandle, child; 221 int i; 222 char buf[20]; 223 uint64_t id; 224 225 ports->dp_dev = self; 226 SLIST_INSERT_HEAD(&fdt_port_devices, ports, dp_list); 227 228 /* 229 * walk the childs looking for ports. ports may be grouped under 230 * an optional ports node 231 */ 232 port_phandle = phandle; 233again: 234 ports->dp_nports = 0; 235 for (child = OF_child(port_phandle); child; child = OF_peer(child)) { 236 if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) 237 continue; 238 if (strcmp(buf, "ports") == 0) { 239 port_phandle = child; 240 goto again; 241 } 242 if (strcmp(buf, "port") != 0) 243 continue; 244 ports->dp_nports++; 245 } 246 if (ports->dp_nports == 0) 247 return 0; 248 249 ports->dp_port = 250 kmem_zalloc(sizeof(struct fdt_port) * ports->dp_nports, KM_SLEEP); 251 KASSERT(ports->dp_port != NULL); 252 /* now scan again ports, looking for endpoints */ 253 for (child = OF_child(port_phandle), i = 0; child; 254 child = OF_peer(child)) { 255 if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) 256 continue; 257 if (strcmp(buf, "ports") == 0) { 258 panic("fdt_ports_register: undetected ports"); 259 } 260 if (strcmp(buf, "port") != 0) 261 continue; 262 if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) { 263 if (ports->dp_nports > 1) 264 aprint_error_dev(self, 265 "%s: missing reg property", 266 fdtbus_get_string(child, "name")); 267 id = i; 268 } 269 ports->dp_port[i].port_id = id; 270 ports->dp_port[i].port_phandle = child; 271 ports->dp_port[i].port_dp = ports; 272 fdt_endpoints_register(child, &ports->dp_port[i], type); 273 i++; 274 } 275 KASSERT(i == ports->dp_nports); 276 return 0; 277} 278 279 280static void 281fdt_endpoints_register(int phandle, struct fdt_port *port, 282 enum endpoint_type type) 283{ 284 int child; 285 int i; 286 char buf[128]; 287 uint64_t id; 288 struct fdt_endpoint *ep, *rep; 289 struct fdt_device_ports *dp; 290 291 port->port_nep = 0; 292 for (child = OF_child(phandle); child; child = OF_peer(child)) { 293 if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) 294 continue; 295 if (strcmp(buf, "endpoint") != 0) 296 continue; 297 port->port_nep++; 298 } 299 if (port->port_nep == 0) { 300 port->port_ep = NULL; 301 return; 302 } 303 304 port->port_ep = 305 kmem_zalloc(sizeof(struct fdt_endpoint) * port->port_nep, KM_SLEEP); 306 KASSERT(port->port_ep != NULL); 307 /* now scan again ports, looking for endpoints */ 308 for (child = OF_child(phandle), i = 0; child; child = OF_peer(child)) { 309 if (OF_getprop(child, "name", buf, sizeof(buf)) <= 0) 310 continue; 311 if (strcmp(buf, "endpoint") != 0) 312 continue; 313 if (fdtbus_get_reg64(child, 0, &id, NULL) != 0) { 314 if (port->port_nep > 1) 315 aprint_error_dev(port->port_dp->dp_dev, 316 "%s: missing reg property", 317 fdtbus_get_string(child, "name")); 318 id = i; 319 } 320 ep = &port->port_ep[i]; 321 ep->ep_id = id; 322 ep->ep_type = type; 323 ep->ep_phandle = child; 324 ep->ep_port = port; 325 ep->ep_rphandle = fdtbus_get_phandle(child, "remote-endpoint"); 326 ep->ep_rep = fdt_endpoint_get_from_phandle( 327 port->port_ep[i].ep_rphandle); 328 rep = ep->ep_rep; 329 if (rep != NULL && rep->ep_rep != NULL) { 330 aprint_error("%s: ", ep_name(ep, buf, sizeof(buf))); 331 aprint_error("remote endpoint %s ", 332 ep_name(rep, buf, sizeof(buf))); 333 aprint_error("already connected to %s\n", 334 ep_name(rep->ep_rep, buf, sizeof(buf))); 335 } else if (rep != NULL) { 336 rep->ep_rep = ep; 337 rep->ep_rphandle = child; 338 aprint_verbose("%s ", ep_name(ep, buf, sizeof(buf))); 339 aprint_verbose("connected to %s\n", 340 ep_name(rep, buf, sizeof(buf))); 341 if (rep->ep_type == EP_OTHER) 342 rep->ep_type = ep->ep_type; 343 else if (ep->ep_type == EP_OTHER) 344 ep->ep_type = rep->ep_type; 345 dp = port->port_dp; 346 if (dp->dp_ep_connect) 347 dp->dp_ep_connect(dp->dp_dev, ep, true); 348 dp = rep->ep_port->port_dp; 349 if (dp->dp_ep_connect) 350 dp->dp_ep_connect(dp->dp_dev, rep, true); 351 } 352 i++; 353 } 354 KASSERT(i == port->port_nep); 355} 356 357static const char * 358ep_name(struct fdt_endpoint *ep, char *buf, int size) 359{ 360 int a; 361 362 a = snprintf(&buf[0], size, "%s", 363 device_xname(ep->ep_port->port_dp->dp_dev)); 364 if (ep->ep_port->port_id >= 0 && a < size) 365 a += snprintf(&buf[a], size - a, " port %d", 366 ep->ep_port->port_id); 367 if (ep->ep_id >= 0 && a < size) 368 snprintf(&buf[a], size - a, " endpoint %d", ep->ep_id); 369 return buf; 370} 371