1 /*- 2 * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 28 #include "opt_platform.h" 29 #include <sys/param.h> 30 #include <sys/kernel.h> 31 #include <sys/kobj.h> 32 #include <sys/lock.h> 33 #include <sys/malloc.h> 34 #include <sys/queue.h> 35 #include <sys/systm.h> 36 #include <sys/sx.h> 37 38 #ifdef FDT 39 #include <dev/ofw/ofw_bus.h> 40 #include <dev/ofw/ofw_bus_subr.h> 41 #endif 42 43 #include <dev/phy/phy.h> 44 #include <dev/phy/phy_internal.h> 45 46 #ifdef FDT 47 #include "phydev_if.h" 48 #endif 49 50 MALLOC_DEFINE(M_PHY, "phy", "Phy framework"); 51 52 /* Default phy methods. */ 53 static int phynode_method_init(struct phynode *phynode); 54 static int phynode_method_enable(struct phynode *phynode, bool disable); 55 static int phynode_method_status(struct phynode *phynode, int *status); 56 57 58 /* 59 * Phy controller methods. 60 */ 61 static phynode_method_t phynode_methods[] = { 62 PHYNODEMETHOD(phynode_init, phynode_method_init), 63 PHYNODEMETHOD(phynode_enable, phynode_method_enable), 64 PHYNODEMETHOD(phynode_status, phynode_method_status), 65 66 PHYNODEMETHOD_END 67 }; 68 DEFINE_CLASS_0(phynode, phynode_class, phynode_methods, 0); 69 70 static phynode_list_t phynode_list = TAILQ_HEAD_INITIALIZER(phynode_list); 71 struct sx phynode_topo_lock; 72 SX_SYSINIT(phy_topology, &phynode_topo_lock, "Phy topology lock"); 73 74 /* ---------------------------------------------------------------------------- 75 * 76 * Default phy methods for base class. 77 * 78 */ 79 80 static int 81 phynode_method_init(struct phynode *phynode) 82 { 83 84 return (0); 85 } 86 87 static int 88 phynode_method_enable(struct phynode *phynode, bool enable) 89 { 90 91 if (!enable) 92 return (ENXIO); 93 94 return (0); 95 } 96 97 static int 98 phynode_method_status(struct phynode *phynode, int *status) 99 { 100 *status = PHY_STATUS_ENABLED; 101 return (0); 102 } 103 104 /* ---------------------------------------------------------------------------- 105 * 106 * Internal functions. 107 * 108 */ 109 /* 110 * Create and initialize phy object, but do not register it. 111 */ 112 struct phynode * 113 phynode_create(device_t pdev, phynode_class_t phynode_class, 114 struct phynode_init_def *def) 115 { 116 struct phynode *phynode; 117 118 119 /* Create object and initialize it. */ 120 phynode = malloc(sizeof(struct phynode), M_PHY, M_WAITOK | M_ZERO); 121 kobj_init((kobj_t)phynode, (kobj_class_t)phynode_class); 122 sx_init(&phynode->lock, "Phy node lock"); 123 124 /* Allocate softc if required. */ 125 if (phynode_class->size > 0) { 126 phynode->softc = malloc(phynode_class->size, M_PHY, 127 M_WAITOK | M_ZERO); 128 } 129 130 /* Rest of init. */ 131 TAILQ_INIT(&phynode->consumers_list); 132 phynode->id = def->id; 133 phynode->pdev = pdev; 134 #ifdef FDT 135 phynode->ofw_node = def->ofw_node; 136 #endif 137 138 return (phynode); 139 } 140 141 /* Register phy object. */ 142 struct phynode * 143 phynode_register(struct phynode *phynode) 144 { 145 int rv; 146 147 #ifdef FDT 148 if (phynode->ofw_node <= 0) 149 phynode->ofw_node = ofw_bus_get_node(phynode->pdev); 150 if (phynode->ofw_node <= 0) 151 return (NULL); 152 #endif 153 154 rv = PHYNODE_INIT(phynode); 155 if (rv != 0) { 156 printf("PHYNODE_INIT failed: %d\n", rv); 157 return (NULL); 158 } 159 160 PHY_TOPO_XLOCK(); 161 TAILQ_INSERT_TAIL(&phynode_list, phynode, phylist_link); 162 PHY_TOPO_UNLOCK(); 163 #ifdef FDT 164 OF_device_register_xref(OF_xref_from_node(phynode->ofw_node), 165 phynode->pdev); 166 #endif 167 return (phynode); 168 } 169 170 static struct phynode * 171 phynode_find_by_id(device_t dev, intptr_t id) 172 { 173 struct phynode *entry; 174 175 PHY_TOPO_ASSERT(); 176 177 TAILQ_FOREACH(entry, &phynode_list, phylist_link) { 178 if ((entry->pdev == dev) && (entry->id == id)) 179 return (entry); 180 } 181 182 return (NULL); 183 } 184 185 /* -------------------------------------------------------------------------- 186 * 187 * Phy providers interface 188 * 189 */ 190 191 void * 192 phynode_get_softc(struct phynode *phynode) 193 { 194 195 return (phynode->softc); 196 } 197 198 device_t 199 phynode_get_device(struct phynode *phynode) 200 { 201 202 return (phynode->pdev); 203 } 204 205 intptr_t phynode_get_id(struct phynode *phynode) 206 { 207 208 return (phynode->id); 209 } 210 211 #ifdef FDT 212 phandle_t 213 phynode_get_ofw_node(struct phynode *phynode) 214 { 215 216 return (phynode->ofw_node); 217 } 218 #endif 219 220 /* -------------------------------------------------------------------------- 221 * 222 * Real consumers executive 223 * 224 */ 225 226 /* 227 * Enable phy. 228 */ 229 int 230 phynode_enable(struct phynode *phynode) 231 { 232 int rv; 233 234 PHY_TOPO_ASSERT(); 235 236 PHYNODE_XLOCK(phynode); 237 if (phynode->enable_cnt == 0) { 238 rv = PHYNODE_ENABLE(phynode, true); 239 if (rv != 0) { 240 PHYNODE_UNLOCK(phynode); 241 return (rv); 242 } 243 } 244 phynode->enable_cnt++; 245 PHYNODE_UNLOCK(phynode); 246 return (0); 247 } 248 249 /* 250 * Disable phy. 251 */ 252 int 253 phynode_disable(struct phynode *phynode) 254 { 255 int rv; 256 257 PHY_TOPO_ASSERT(); 258 259 PHYNODE_XLOCK(phynode); 260 if (phynode->enable_cnt == 1) { 261 rv = PHYNODE_ENABLE(phynode, false); 262 if (rv != 0) { 263 PHYNODE_UNLOCK(phynode); 264 return (rv); 265 } 266 } 267 phynode->enable_cnt--; 268 PHYNODE_UNLOCK(phynode); 269 return (0); 270 } 271 272 /* 273 * Set phy mode (protocol and its variant). 274 */ 275 int 276 phynode_set_mode(struct phynode *phynode, phy_mode_t mode, 277 phy_submode_t submode) 278 { 279 int rv; 280 281 PHY_TOPO_ASSERT(); 282 283 PHYNODE_XLOCK(phynode); 284 rv = PHYNODE_SET_MODE(phynode, mode, submode); 285 PHYNODE_UNLOCK(phynode); 286 return (rv); 287 } 288 289 /* 290 * Get phy status. (PHY_STATUS_*) 291 */ 292 int 293 phynode_status(struct phynode *phynode, int *status) 294 { 295 int rv; 296 297 PHY_TOPO_ASSERT(); 298 299 PHYNODE_XLOCK(phynode); 300 rv = PHYNODE_STATUS(phynode, status); 301 PHYNODE_UNLOCK(phynode); 302 return (rv); 303 } 304 305 /* -------------------------------------------------------------------------- 306 * 307 * Phy consumers interface. 308 * 309 */ 310 311 /* Helper function for phy_get*() */ 312 static phy_t 313 phy_create(struct phynode *phynode, device_t cdev) 314 { 315 struct phy *phy; 316 317 PHY_TOPO_ASSERT(); 318 319 phy = malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO); 320 phy->cdev = cdev; 321 phy->phynode = phynode; 322 phy->enable_cnt = 0; 323 324 PHYNODE_XLOCK(phynode); 325 phynode->ref_cnt++; 326 TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link); 327 PHYNODE_UNLOCK(phynode); 328 329 return (phy); 330 } 331 332 int 333 phy_enable(phy_t phy) 334 { 335 int rv; 336 struct phynode *phynode; 337 338 phynode = phy->phynode; 339 KASSERT(phynode->ref_cnt > 0, 340 ("Attempt to access unreferenced phy.\n")); 341 342 PHY_TOPO_SLOCK(); 343 rv = phynode_enable(phynode); 344 if (rv == 0) 345 phy->enable_cnt++; 346 PHY_TOPO_UNLOCK(); 347 return (rv); 348 } 349 350 int 351 phy_disable(phy_t phy) 352 { 353 int rv; 354 struct phynode *phynode; 355 356 phynode = phy->phynode; 357 KASSERT(phynode->ref_cnt > 0, 358 ("Attempt to access unreferenced phy.\n")); 359 KASSERT(phy->enable_cnt > 0, 360 ("Attempt to disable already disabled phy.\n")); 361 362 PHY_TOPO_SLOCK(); 363 rv = phynode_disable(phynode); 364 if (rv == 0) 365 phy->enable_cnt--; 366 PHY_TOPO_UNLOCK(); 367 return (rv); 368 } 369 370 int 371 phy_set_mode(phy_t phy, phy_mode_t mode, phy_submode_t submode) 372 { 373 int rv; 374 struct phynode *phynode; 375 376 phynode = phy->phynode; 377 KASSERT(phynode->ref_cnt > 0, 378 ("Attempt to access unreferenced phy.\n")); 379 380 PHY_TOPO_SLOCK(); 381 rv = phynode_set_mode(phynode, mode, submode); 382 PHY_TOPO_UNLOCK(); 383 return (rv); 384 } 385 386 int 387 phy_status(phy_t phy, int *status) 388 { 389 int rv; 390 struct phynode *phynode; 391 392 phynode = phy->phynode; 393 KASSERT(phynode->ref_cnt > 0, 394 ("Attempt to access unreferenced phy.\n")); 395 396 PHY_TOPO_SLOCK(); 397 rv = phynode_status(phynode, status); 398 PHY_TOPO_UNLOCK(); 399 return (rv); 400 } 401 402 int 403 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, 404 phy_t *phy) 405 { 406 struct phynode *phynode; 407 408 PHY_TOPO_SLOCK(); 409 410 phynode = phynode_find_by_id(provider_dev, id); 411 if (phynode == NULL) { 412 PHY_TOPO_UNLOCK(); 413 return (ENODEV); 414 } 415 *phy = phy_create(phynode, consumer_dev); 416 PHY_TOPO_UNLOCK(); 417 418 return (0); 419 } 420 421 void 422 phy_release(phy_t phy) 423 { 424 struct phynode *phynode; 425 426 phynode = phy->phynode; 427 KASSERT(phynode->ref_cnt > 0, 428 ("Attempt to access unreferenced phy.\n")); 429 430 PHY_TOPO_SLOCK(); 431 while (phy->enable_cnt > 0) { 432 phynode_disable(phynode); 433 phy->enable_cnt--; 434 } 435 PHYNODE_XLOCK(phynode); 436 TAILQ_REMOVE(&phynode->consumers_list, phy, link); 437 phynode->ref_cnt--; 438 PHYNODE_UNLOCK(phynode); 439 PHY_TOPO_UNLOCK(); 440 441 free(phy, M_PHY); 442 } 443 444 #ifdef FDT 445 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells, 446 pcell_t *cells, intptr_t *id) 447 { 448 struct phynode *entry; 449 phandle_t node; 450 451 /* Single device can register multiple subnodes. */ 452 if (ncells == 0) { 453 454 node = OF_node_from_xref(xref); 455 PHY_TOPO_XLOCK(); 456 TAILQ_FOREACH(entry, &phynode_list, phylist_link) { 457 if ((entry->pdev == provider) && 458 (entry->ofw_node == node)) { 459 *id = entry->id; 460 PHY_TOPO_UNLOCK(); 461 return (0); 462 } 463 } 464 PHY_TOPO_UNLOCK(); 465 return (ERANGE); 466 } 467 468 /* First cell is ID. */ 469 if (ncells == 1) { 470 *id = cells[0]; 471 return (0); 472 } 473 474 /* No default way how to get ID, custom mapper is required. */ 475 return (ERANGE); 476 } 477 478 int 479 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy) 480 { 481 phandle_t xnode; 482 pcell_t *cells; 483 device_t phydev; 484 int ncells, rv; 485 intptr_t id; 486 487 if (cnode <= 0) 488 cnode = ofw_bus_get_node(consumer_dev); 489 if (cnode <= 0) { 490 device_printf(consumer_dev, 491 "%s called on not ofw based device\n", __func__); 492 return (ENXIO); 493 } 494 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, 495 &xnode, &ncells, &cells); 496 if (rv != 0) 497 return (rv); 498 499 /* Tranlate provider to device. */ 500 phydev = OF_device_from_xref(xnode); 501 if (phydev == NULL) { 502 OF_prop_free(cells); 503 return (ENODEV); 504 } 505 /* Map phy to number. */ 506 rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id); 507 OF_prop_free(cells); 508 if (rv != 0) 509 return (rv); 510 511 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 512 } 513 514 int 515 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name, 516 phy_t *phy) 517 { 518 int rv, idx; 519 520 if (cnode <= 0) 521 cnode = ofw_bus_get_node(consumer_dev); 522 if (cnode <= 0) { 523 device_printf(consumer_dev, 524 "%s called on not ofw based device\n", __func__); 525 return (ENXIO); 526 } 527 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); 528 if (rv != 0) 529 return (rv); 530 return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy)); 531 } 532 533 int 534 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name, 535 phy_t *phy) 536 { 537 pcell_t *cells; 538 device_t phydev; 539 int ncells, rv; 540 intptr_t id; 541 542 if (cnode <= 0) 543 cnode = ofw_bus_get_node(consumer_dev); 544 if (cnode <= 0) { 545 device_printf(consumer_dev, 546 "%s called on not ofw based device\n", __func__); 547 return (ENXIO); 548 } 549 ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t), 550 (void **)&cells); 551 if (ncells < 1) 552 return (ENOENT); 553 554 /* Tranlate provider to device. */ 555 phydev = OF_device_from_xref(cells[0]); 556 if (phydev == NULL) { 557 OF_prop_free(cells); 558 return (ENODEV); 559 } 560 /* Map phy to number. */ 561 rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); 562 OF_prop_free(cells); 563 if (rv != 0) 564 return (rv); 565 566 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 567 } 568 #endif 569