1 /*- 2 * Copyright 2016 Michal Meloun <[email protected]> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_platform.h" 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/kobj.h> 34 #include <sys/lock.h> 35 #include <sys/malloc.h> 36 #include <sys/queue.h> 37 #include <sys/systm.h> 38 #include <sys/sx.h> 39 40 #ifdef FDT 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 #endif 44 45 #include <dev/extres/phy/phy.h> 46 #include <dev/extres/phy/phy_internal.h> 47 48 #include "phydev_if.h" 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 /* 274 * Get phy status. (PHY_STATUS_*) 275 */ 276 int 277 phynode_status(struct phynode *phynode, int *status) 278 { 279 int rv; 280 281 PHY_TOPO_ASSERT(); 282 283 PHYNODE_XLOCK(phynode); 284 rv = PHYNODE_STATUS(phynode, status); 285 PHYNODE_UNLOCK(phynode); 286 return (rv); 287 } 288 289 /* -------------------------------------------------------------------------- 290 * 291 * Phy consumers interface. 292 * 293 */ 294 295 /* Helper function for phy_get*() */ 296 static phy_t 297 phy_create(struct phynode *phynode, device_t cdev) 298 { 299 struct phy *phy; 300 301 PHY_TOPO_ASSERT(); 302 303 phy = malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO); 304 phy->cdev = cdev; 305 phy->phynode = phynode; 306 phy->enable_cnt = 0; 307 308 PHYNODE_XLOCK(phynode); 309 phynode->ref_cnt++; 310 TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link); 311 PHYNODE_UNLOCK(phynode); 312 313 return (phy); 314 } 315 316 int 317 phy_enable(phy_t phy) 318 { 319 int rv; 320 struct phynode *phynode; 321 322 phynode = phy->phynode; 323 KASSERT(phynode->ref_cnt > 0, 324 ("Attempt to access unreferenced phy.\n")); 325 326 PHY_TOPO_SLOCK(); 327 rv = phynode_enable(phynode); 328 if (rv == 0) 329 phy->enable_cnt++; 330 PHY_TOPO_UNLOCK(); 331 return (rv); 332 } 333 334 int 335 phy_disable(phy_t phy) 336 { 337 int rv; 338 struct phynode *phynode; 339 340 phynode = phy->phynode; 341 KASSERT(phynode->ref_cnt > 0, 342 ("Attempt to access unreferenced phy.\n")); 343 KASSERT(phy->enable_cnt > 0, 344 ("Attempt to disable already disabled phy.\n")); 345 346 PHY_TOPO_SLOCK(); 347 rv = phynode_disable(phynode); 348 if (rv == 0) 349 phy->enable_cnt--; 350 PHY_TOPO_UNLOCK(); 351 return (rv); 352 } 353 354 int 355 phy_status(phy_t phy, int *status) 356 { 357 int rv; 358 struct phynode *phynode; 359 360 phynode = phy->phynode; 361 KASSERT(phynode->ref_cnt > 0, 362 ("Attempt to access unreferenced phy.\n")); 363 364 PHY_TOPO_SLOCK(); 365 rv = phynode_status(phynode, status); 366 PHY_TOPO_UNLOCK(); 367 return (rv); 368 } 369 370 int 371 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, 372 phy_t *phy) 373 { 374 struct phynode *phynode; 375 376 PHY_TOPO_SLOCK(); 377 378 phynode = phynode_find_by_id(provider_dev, id); 379 if (phynode == NULL) { 380 PHY_TOPO_UNLOCK(); 381 return (ENODEV); 382 } 383 *phy = phy_create(phynode, consumer_dev); 384 PHY_TOPO_UNLOCK(); 385 386 return (0); 387 } 388 389 void 390 phy_release(phy_t phy) 391 { 392 struct phynode *phynode; 393 394 phynode = phy->phynode; 395 KASSERT(phynode->ref_cnt > 0, 396 ("Attempt to access unreferenced phy.\n")); 397 398 PHY_TOPO_SLOCK(); 399 while (phy->enable_cnt > 0) { 400 phynode_disable(phynode); 401 phy->enable_cnt--; 402 } 403 PHYNODE_XLOCK(phynode); 404 TAILQ_REMOVE(&phynode->consumers_list, phy, link); 405 phynode->ref_cnt--; 406 PHYNODE_UNLOCK(phynode); 407 PHY_TOPO_UNLOCK(); 408 409 free(phy, M_PHY); 410 } 411 412 #ifdef FDT 413 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells, 414 pcell_t *cells, intptr_t *id) 415 { 416 struct phynode *entry; 417 phandle_t node; 418 419 /* Single device can register multiple subnodes. */ 420 if (ncells == 0) { 421 422 node = OF_node_from_xref(xref); 423 PHY_TOPO_XLOCK(); 424 TAILQ_FOREACH(entry, &phynode_list, phylist_link) { 425 if ((entry->pdev == provider) && 426 (entry->ofw_node == node)) { 427 *id = entry->id; 428 PHY_TOPO_UNLOCK(); 429 return (0); 430 } 431 } 432 PHY_TOPO_UNLOCK(); 433 return (ERANGE); 434 } 435 436 /* First cell is ID. */ 437 if (ncells == 1) { 438 *id = cells[0]; 439 return (0); 440 } 441 442 /* No default way how to get ID, custom mapper is required. */ 443 return (ERANGE); 444 } 445 446 int 447 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy) 448 { 449 phandle_t xnode; 450 pcell_t *cells; 451 device_t phydev; 452 int ncells, rv; 453 intptr_t id; 454 455 if (cnode <= 0) 456 cnode = ofw_bus_get_node(consumer_dev); 457 if (cnode <= 0) { 458 device_printf(consumer_dev, 459 "%s called on not ofw based device\n", __func__); 460 return (ENXIO); 461 } 462 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, 463 &xnode, &ncells, &cells); 464 if (rv != 0) 465 return (rv); 466 467 /* Tranlate provider to device. */ 468 phydev = OF_device_from_xref(xnode); 469 if (phydev == NULL) { 470 OF_prop_free(cells); 471 return (ENODEV); 472 } 473 /* Map phy to number. */ 474 rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id); 475 OF_prop_free(cells); 476 if (rv != 0) 477 return (rv); 478 479 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 480 } 481 482 int 483 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name, 484 phy_t *phy) 485 { 486 int rv, idx; 487 488 if (cnode <= 0) 489 cnode = ofw_bus_get_node(consumer_dev); 490 if (cnode <= 0) { 491 device_printf(consumer_dev, 492 "%s called on not ofw based device\n", __func__); 493 return (ENXIO); 494 } 495 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); 496 if (rv != 0) 497 return (rv); 498 return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy)); 499 } 500 501 int 502 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name, 503 phy_t *phy) 504 { 505 pcell_t *cells; 506 device_t phydev; 507 int ncells, rv; 508 intptr_t id; 509 510 if (cnode <= 0) 511 cnode = ofw_bus_get_node(consumer_dev); 512 if (cnode <= 0) { 513 device_printf(consumer_dev, 514 "%s called on not ofw based device\n", __func__); 515 return (ENXIO); 516 } 517 ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t), 518 (void **)&cells); 519 if (ncells < 1) 520 return (ENOENT); 521 522 /* Tranlate provider to device. */ 523 phydev = OF_device_from_xref(cells[0]); 524 if (phydev == NULL) { 525 OF_prop_free(cells); 526 return (ENODEV); 527 } 528 /* Map phy to number. */ 529 rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); 530 OF_prop_free(cells); 531 if (rv != 0) 532 return (rv); 533 534 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 535 } 536 #endif 537