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