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