1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2016,2017 SoftIron Inc. 5 * Copyright (c) 2020 Advanced Micro Devices, Inc. 6 * 7 * This software was developed by Andrew Turner under 8 * the sponsorship of SoftIron Inc. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/module.h> 39 #include <sys/mutex.h> 40 #include <sys/queue.h> 41 #include <sys/rman.h> 42 #include <sys/socket.h> 43 #include <sys/sockio.h> 44 #include <sys/sx.h> 45 #include <sys/taskqueue.h> 46 47 #include <net/ethernet.h> 48 #include <net/if.h> 49 #include <net/if_var.h> 50 #include <net/if_media.h> 51 #include <net/if_types.h> 52 53 #include <dev/ofw/openfirm.h> 54 #include <dev/ofw/ofw_bus.h> 55 #include <dev/ofw/ofw_bus_subr.h> 56 57 #include <machine/bus.h> 58 59 #include "miibus_if.h" 60 61 #include "xgbe.h" 62 #include "xgbe-common.h" 63 64 static device_probe_t axgbe_probe; 65 static device_attach_t axgbe_attach; 66 67 struct axgbe_softc { 68 /* Must be first */ 69 struct xgbe_prv_data prv; 70 71 uint8_t mac_addr[ETHER_ADDR_LEN]; 72 struct ifmedia media; 73 }; 74 75 static struct ofw_compat_data compat_data[] = { 76 { "amd,xgbe-seattle-v1a", true }, 77 { NULL, false } 78 }; 79 80 static struct resource_spec old_phy_spec[] = { 81 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Rx/Tx regs */ 82 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Integration regs */ 83 { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* Integration regs */ 84 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Interrupt */ 85 { -1, 0 } 86 }; 87 88 static struct resource_spec old_mac_spec[] = { 89 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* MAC regs */ 90 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* PCS regs */ 91 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Device interrupt */ 92 /* Per-channel interrupts */ 93 { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL }, 94 { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, 95 { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, 96 { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, 97 { -1, 0 } 98 }; 99 100 static struct resource_spec mac_spec[] = { 101 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* MAC regs */ 102 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* PCS regs */ 103 { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* Rx/Tx regs */ 104 { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* Integration regs */ 105 { SYS_RES_MEMORY, 4, RF_ACTIVE }, /* Integration regs */ 106 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Device interrupt */ 107 /* Per-channel and auto-negotiation interrupts */ 108 { SYS_RES_IRQ, 1, RF_ACTIVE }, 109 { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, 110 { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, 111 { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, 112 { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, 113 { -1, 0 } 114 }; 115 116 static struct xgbe_version_data xgbe_v1 = { 117 .init_function_ptrs_phy_impl = xgbe_init_function_ptrs_phy_v1, 118 .xpcs_access = XGBE_XPCS_ACCESS_V1, 119 .tx_max_fifo_size = 81920, 120 .rx_max_fifo_size = 81920, 121 .tx_tstamp_workaround = 1, 122 }; 123 124 MALLOC_DEFINE(M_AXGBE, "axgbe", "axgbe data"); 125 126 static void 127 axgbe_init(void *p) 128 { 129 struct axgbe_softc *sc; 130 if_t ifp; 131 132 sc = p; 133 ifp = sc->prv.netdev; 134 if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 135 return; 136 137 if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); 138 } 139 140 static int 141 axgbe_ioctl(if_t ifp, unsigned long command, caddr_t data) 142 { 143 struct axgbe_softc *sc = if_getsoftc(ifp); 144 struct ifreq *ifr = (struct ifreq *)data; 145 int error = 0; 146 147 switch(command) { 148 case SIOCSIFMTU: 149 if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU_JUMBO) 150 error = EINVAL; 151 /* TODO - change it to iflib way */ 152 break; 153 case SIOCSIFFLAGS: 154 error = 0; 155 break; 156 case SIOCSIFMEDIA: 157 case SIOCGIFMEDIA: 158 error = ifmedia_ioctl(ifp, ifr, &sc->media, command); 159 break; 160 default: 161 error = ether_ioctl(ifp, command, data); 162 break; 163 } 164 165 return (error); 166 } 167 168 static void 169 axgbe_qflush(if_t ifp) 170 { 171 172 if_qflush(ifp); 173 } 174 175 static int 176 axgbe_media_change(if_t ifp) 177 { 178 struct axgbe_softc *sc; 179 int cur_media; 180 181 sc = if_getsoftc(ifp); 182 183 sx_xlock(&sc->prv.an_mutex); 184 cur_media = sc->media.ifm_cur->ifm_media; 185 186 switch (IFM_SUBTYPE(cur_media)) { 187 case IFM_10G_KR: 188 sc->prv.phy.speed = SPEED_10000; 189 sc->prv.phy.autoneg = AUTONEG_DISABLE; 190 break; 191 case IFM_2500_KX: 192 sc->prv.phy.speed = SPEED_2500; 193 sc->prv.phy.autoneg = AUTONEG_DISABLE; 194 break; 195 case IFM_1000_KX: 196 sc->prv.phy.speed = SPEED_1000; 197 sc->prv.phy.autoneg = AUTONEG_DISABLE; 198 break; 199 case IFM_AUTO: 200 sc->prv.phy.autoneg = AUTONEG_ENABLE; 201 break; 202 } 203 sx_xunlock(&sc->prv.an_mutex); 204 205 return (-sc->prv.phy_if.phy_config_aneg(&sc->prv)); 206 } 207 208 static void 209 axgbe_media_status(if_t ifp, struct ifmediareq *ifmr) 210 { 211 struct axgbe_softc *sc; 212 213 sc = if_getsoftc(ifp); 214 215 ifmr->ifm_status = IFM_AVALID; 216 if (!sc->prv.phy.link) 217 return; 218 219 ifmr->ifm_status |= IFM_ACTIVE; 220 ifmr->ifm_active = IFM_ETHER; 221 222 if (sc->prv.phy.duplex == DUPLEX_FULL) 223 ifmr->ifm_active |= IFM_FDX; 224 else 225 ifmr->ifm_active |= IFM_HDX; 226 227 switch (sc->prv.phy.speed) { 228 case SPEED_10000: 229 ifmr->ifm_active |= IFM_10G_KR; 230 break; 231 case SPEED_2500: 232 ifmr->ifm_active |= IFM_2500_KX; 233 break; 234 case SPEED_1000: 235 ifmr->ifm_active |= IFM_1000_KX; 236 break; 237 } 238 } 239 240 static uint64_t 241 axgbe_get_counter(if_t ifp, ift_counter c) 242 { 243 struct xgbe_prv_data *pdata = if_getsoftc(ifp); 244 struct xgbe_mmc_stats *pstats = &pdata->mmc_stats; 245 246 DBGPR("-->%s\n", __func__); 247 248 pdata->hw_if.read_mmc_stats(pdata); 249 250 switch(c) { 251 case IFCOUNTER_IPACKETS: 252 return (pstats->rxframecount_gb); 253 case IFCOUNTER_IERRORS: 254 return (pstats->rxframecount_gb - 255 pstats->rxbroadcastframes_g - 256 pstats->rxmulticastframes_g - 257 pstats->rxunicastframes_g); 258 case IFCOUNTER_OPACKETS: 259 return (pstats->txframecount_gb); 260 case IFCOUNTER_OERRORS: 261 return (pstats->txframecount_gb - pstats->txframecount_g); 262 case IFCOUNTER_IBYTES: 263 return (pstats->rxoctetcount_gb); 264 case IFCOUNTER_OBYTES: 265 return (pstats->txoctetcount_gb); 266 default: 267 return (if_get_counter_default(ifp, c)); 268 } 269 } 270 271 static int 272 axgbe_probe(device_t dev) 273 { 274 275 if (!ofw_bus_status_okay(dev)) 276 return (ENXIO); 277 278 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 279 return (ENXIO); 280 281 device_set_desc(dev, "AMD 10 Gigabit Ethernet"); 282 return (BUS_PROBE_DEFAULT); 283 } 284 285 static int 286 axgbe_get_optional_prop(device_t dev, phandle_t node, const char *name, 287 int *data, size_t len) 288 { 289 290 if (!OF_hasprop(node, name)) 291 return (-1); 292 293 if (OF_getencprop(node, name, data, len) <= 0) { 294 device_printf(dev,"%s property is invalid\n", name); 295 return (ENXIO); 296 } 297 298 return (0); 299 } 300 301 static int 302 axgbe_attach(device_t dev) 303 { 304 struct axgbe_softc *sc; 305 if_t ifp; 306 pcell_t phy_handle; 307 device_t phydev; 308 phandle_t node, phy_node; 309 struct resource *mac_res[11]; 310 struct resource *phy_res[4]; 311 ssize_t len; 312 int error, i, j; 313 314 sc = device_get_softc(dev); 315 316 sc->prv.vdata = &xgbe_v1; 317 node = ofw_bus_get_node(dev); 318 if (OF_getencprop(node, "phy-handle", &phy_handle, 319 sizeof(phy_handle)) <= 0) { 320 phy_node = node; 321 322 if (bus_alloc_resources(dev, mac_spec, mac_res)) { 323 device_printf(dev, 324 "could not allocate phy resources\n"); 325 return (ENXIO); 326 } 327 328 sc->prv.xgmac_res = mac_res[0]; 329 sc->prv.xpcs_res = mac_res[1]; 330 sc->prv.rxtx_res = mac_res[2]; 331 sc->prv.sir0_res = mac_res[3]; 332 sc->prv.sir1_res = mac_res[4]; 333 334 sc->prv.dev_irq_res = mac_res[5]; 335 sc->prv.per_channel_irq = OF_hasprop(node, 336 XGBE_DMA_IRQS_PROPERTY); 337 for (i = 0, j = 6; j < nitems(mac_res) - 1 && 338 mac_res[j + 1] != NULL; i++, j++) { 339 if (sc->prv.per_channel_irq) { 340 sc->prv.chan_irq_res[i] = mac_res[j]; 341 } 342 } 343 344 /* The last entry is the auto-negotiation interrupt */ 345 sc->prv.an_irq_res = mac_res[j]; 346 } else { 347 phydev = OF_device_from_xref(phy_handle); 348 phy_node = ofw_bus_get_node(phydev); 349 350 if (bus_alloc_resources(phydev, old_phy_spec, phy_res)) { 351 device_printf(dev, 352 "could not allocate phy resources\n"); 353 return (ENXIO); 354 } 355 356 if (bus_alloc_resources(dev, old_mac_spec, mac_res)) { 357 device_printf(dev, 358 "could not allocate mac resources\n"); 359 return (ENXIO); 360 } 361 362 sc->prv.rxtx_res = phy_res[0]; 363 sc->prv.sir0_res = phy_res[1]; 364 sc->prv.sir1_res = phy_res[2]; 365 sc->prv.an_irq_res = phy_res[3]; 366 367 sc->prv.xgmac_res = mac_res[0]; 368 sc->prv.xpcs_res = mac_res[1]; 369 sc->prv.dev_irq_res = mac_res[2]; 370 sc->prv.per_channel_irq = OF_hasprop(node, 371 XGBE_DMA_IRQS_PROPERTY); 372 if (sc->prv.per_channel_irq) { 373 for (i = 0, j = 3; i < nitems(sc->prv.chan_irq_res) && 374 mac_res[j] != NULL; i++, j++) { 375 sc->prv.chan_irq_res[i] = mac_res[j]; 376 } 377 } 378 } 379 380 if ((len = OF_getproplen(node, "mac-address")) < 0) { 381 device_printf(dev, "No mac-address property\n"); 382 return (EINVAL); 383 } 384 385 if (len != ETHER_ADDR_LEN) 386 return (EINVAL); 387 388 OF_getprop(node, "mac-address", sc->mac_addr, ETHER_ADDR_LEN); 389 390 sc->prv.netdev = ifp = if_alloc(IFT_ETHER); 391 sc->prv.dev = dev; 392 sc->prv.dmat = bus_get_dma_tag(dev); 393 sc->prv.phy.advertising = ADVERTISED_10000baseKR_Full | 394 ADVERTISED_1000baseKX_Full; 395 396 397 /* 398 * Read the needed properties from the phy node. 399 */ 400 401 /* This is documented as optional, but Linux requires it */ 402 if (OF_getencprop(phy_node, XGBE_SPEEDSET_PROPERTY, &sc->prv.speed_set, 403 sizeof(sc->prv.speed_set)) <= 0) { 404 device_printf(dev, "%s property is missing\n", 405 XGBE_SPEEDSET_PROPERTY); 406 return (EINVAL); 407 } 408 409 error = axgbe_get_optional_prop(dev, phy_node, XGBE_BLWC_PROPERTY, 410 sc->prv.serdes_blwc, sizeof(sc->prv.serdes_blwc)); 411 if (error > 0) { 412 return (error); 413 } else if (error < 0) { 414 sc->prv.serdes_blwc[0] = XGBE_SPEED_1000_BLWC; 415 sc->prv.serdes_blwc[1] = XGBE_SPEED_2500_BLWC; 416 sc->prv.serdes_blwc[2] = XGBE_SPEED_10000_BLWC; 417 } 418 419 error = axgbe_get_optional_prop(dev, phy_node, XGBE_CDR_RATE_PROPERTY, 420 sc->prv.serdes_cdr_rate, sizeof(sc->prv.serdes_cdr_rate)); 421 if (error > 0) { 422 return (error); 423 } else if (error < 0) { 424 sc->prv.serdes_cdr_rate[0] = XGBE_SPEED_1000_CDR; 425 sc->prv.serdes_cdr_rate[1] = XGBE_SPEED_2500_CDR; 426 sc->prv.serdes_cdr_rate[2] = XGBE_SPEED_10000_CDR; 427 } 428 429 error = axgbe_get_optional_prop(dev, phy_node, XGBE_PQ_SKEW_PROPERTY, 430 sc->prv.serdes_pq_skew, sizeof(sc->prv.serdes_pq_skew)); 431 if (error > 0) { 432 return (error); 433 } else if (error < 0) { 434 sc->prv.serdes_pq_skew[0] = XGBE_SPEED_1000_PQ; 435 sc->prv.serdes_pq_skew[1] = XGBE_SPEED_2500_PQ; 436 sc->prv.serdes_pq_skew[2] = XGBE_SPEED_10000_PQ; 437 } 438 439 error = axgbe_get_optional_prop(dev, phy_node, XGBE_TX_AMP_PROPERTY, 440 sc->prv.serdes_tx_amp, sizeof(sc->prv.serdes_tx_amp)); 441 if (error > 0) { 442 return (error); 443 } else if (error < 0) { 444 sc->prv.serdes_tx_amp[0] = XGBE_SPEED_1000_TXAMP; 445 sc->prv.serdes_tx_amp[1] = XGBE_SPEED_2500_TXAMP; 446 sc->prv.serdes_tx_amp[2] = XGBE_SPEED_10000_TXAMP; 447 } 448 449 error = axgbe_get_optional_prop(dev, phy_node, XGBE_DFE_CFG_PROPERTY, 450 sc->prv.serdes_dfe_tap_cfg, sizeof(sc->prv.serdes_dfe_tap_cfg)); 451 if (error > 0) { 452 return (error); 453 } else if (error < 0) { 454 sc->prv.serdes_dfe_tap_cfg[0] = XGBE_SPEED_1000_DFE_TAP_CONFIG; 455 sc->prv.serdes_dfe_tap_cfg[1] = XGBE_SPEED_2500_DFE_TAP_CONFIG; 456 sc->prv.serdes_dfe_tap_cfg[2] = XGBE_SPEED_10000_DFE_TAP_CONFIG; 457 } 458 459 error = axgbe_get_optional_prop(dev, phy_node, XGBE_DFE_ENA_PROPERTY, 460 sc->prv.serdes_dfe_tap_ena, sizeof(sc->prv.serdes_dfe_tap_ena)); 461 if (error > 0) { 462 return (error); 463 } else if (error < 0) { 464 sc->prv.serdes_dfe_tap_ena[0] = XGBE_SPEED_1000_DFE_TAP_ENABLE; 465 sc->prv.serdes_dfe_tap_ena[1] = XGBE_SPEED_2500_DFE_TAP_ENABLE; 466 sc->prv.serdes_dfe_tap_ena[2] = XGBE_SPEED_10000_DFE_TAP_ENABLE; 467 } 468 469 /* Check if the NIC is DMA coherent */ 470 sc->prv.coherent = OF_hasprop(node, "dma-coherent"); 471 if (sc->prv.coherent) { 472 sc->prv.arcr = XGBE_DMA_OS_ARCR; 473 sc->prv.awcr = XGBE_DMA_OS_AWCR; 474 } else { 475 sc->prv.arcr = XGBE_DMA_SYS_ARCR; 476 sc->prv.awcr = XGBE_DMA_SYS_AWCR; 477 } 478 479 /* Create the lock & workqueues */ 480 spin_lock_init(&sc->prv.xpcs_lock); 481 sc->prv.dev_workqueue = taskqueue_create("axgbe", M_WAITOK, 482 taskqueue_thread_enqueue, &sc->prv.dev_workqueue); 483 taskqueue_start_threads(&sc->prv.dev_workqueue, 1, PI_NET, 484 "axgbe taskq"); 485 486 /* Set the needed pointers */ 487 xgbe_init_function_ptrs_phy(&sc->prv.phy_if); 488 xgbe_init_function_ptrs_dev(&sc->prv.hw_if); 489 xgbe_init_function_ptrs_desc(&sc->prv.desc_if); 490 sc->prv.vdata->init_function_ptrs_phy_impl(&sc->prv.phy_if); 491 492 /* Reset the hardware */ 493 sc->prv.hw_if.exit(&sc->prv); 494 495 /* Read the hardware features */ 496 xgbe_get_all_hw_features(&sc->prv); 497 498 /* Set default values */ 499 sc->prv.tx_desc_count = XGBE_TX_DESC_CNT; 500 sc->prv.tx_sf_mode = MTL_TSF_ENABLE; 501 sc->prv.tx_threshold = MTL_TX_THRESHOLD_64; 502 sc->prv.tx_osp_mode = DMA_OSP_ENABLE; 503 sc->prv.rx_desc_count = XGBE_RX_DESC_CNT; 504 sc->prv.rx_sf_mode = MTL_RSF_DISABLE; 505 sc->prv.rx_threshold = MTL_RX_THRESHOLD_64; 506 sc->prv.pbl = DMA_PBL_128; 507 sc->prv.pause_autoneg = 1; 508 sc->prv.tx_pause = 1; 509 sc->prv.rx_pause = 1; 510 sc->prv.phy_speed = SPEED_UNKNOWN; 511 sc->prv.power_down = 0; 512 513 /* TODO: Limit to min(ncpus, hw rings) */ 514 sc->prv.tx_ring_count = 1; 515 sc->prv.tx_q_count = 1; 516 sc->prv.rx_ring_count = 1; 517 sc->prv.rx_q_count = sc->prv.hw_feat.rx_q_cnt; 518 519 /* Init the PHY */ 520 sc->prv.phy_if.phy_init(&sc->prv); 521 522 /* Set the coalescing */ 523 xgbe_init_rx_coalesce(&sc->prv); 524 xgbe_init_tx_coalesce(&sc->prv); 525 526 if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 527 if_setinitfn(ifp, axgbe_init); 528 if_setsoftc(ifp, sc); 529 if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 530 if_setioctlfn(ifp, axgbe_ioctl); 531 /* TODO - change it to iflib way */ 532 if_setqflushfn(ifp, axgbe_qflush); 533 if_setgetcounterfn(ifp, axgbe_get_counter); 534 535 /* TODO: Support HW offload */ 536 if_setcapabilities(ifp, 0); 537 if_setcapenable(ifp, 0); 538 if_sethwassist(ifp, 0); 539 540 ether_ifattach(ifp, sc->mac_addr); 541 542 ifmedia_init(&sc->media, IFM_IMASK, axgbe_media_change, 543 axgbe_media_status); 544 #ifdef notyet 545 ifmedia_add(&sc->media, IFM_ETHER | IFM_10G_KR, 0, NULL); 546 #endif 547 ifmedia_add(&sc->media, IFM_ETHER | IFM_1000_KX, 0, NULL); 548 ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); 549 ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); 550 551 set_bit(XGBE_DOWN, &sc->prv.dev_state); 552 553 /* TODO - change it to iflib way */ 554 return (0); 555 } 556 557 static device_method_t axgbe_methods[] = { 558 /* Device interface */ 559 DEVMETHOD(device_probe, axgbe_probe), 560 DEVMETHOD(device_attach, axgbe_attach), 561 562 { 0, 0 } 563 }; 564 565 DEFINE_CLASS_0(axgbe, axgbe_driver, axgbe_methods, 566 sizeof(struct axgbe_softc)); 567 DRIVER_MODULE(axa, simplebus, axgbe_driver, 0, 0); 568 569 570 static struct ofw_compat_data phy_compat_data[] = { 571 { "amd,xgbe-phy-seattle-v1a", true }, 572 { NULL, false } 573 }; 574 575 static int 576 axgbephy_probe(device_t dev) 577 { 578 579 if (!ofw_bus_status_okay(dev)) 580 return (ENXIO); 581 582 if (!ofw_bus_search_compatible(dev, phy_compat_data)->ocd_data) 583 return (ENXIO); 584 585 device_set_desc(dev, "AMD 10 Gigabit Ethernet"); 586 return (BUS_PROBE_DEFAULT); 587 } 588 589 static int 590 axgbephy_attach(device_t dev) 591 { 592 phandle_t node; 593 594 node = ofw_bus_get_node(dev); 595 OF_device_register_xref(OF_xref_from_node(node), dev); 596 597 return (0); 598 } 599 600 static device_method_t axgbephy_methods[] = { 601 /* Device interface */ 602 DEVMETHOD(device_probe, axgbephy_probe), 603 DEVMETHOD(device_attach, axgbephy_attach), 604 605 { 0, 0 } 606 }; 607 608 DEFINE_CLASS_0(axgbephy, axgbephy_driver, axgbephy_methods, 0); 609 EARLY_DRIVER_MODULE(axgbephy, simplebus, axgbephy_driver, 610 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); 611