xref: /freebsd/sys/dev/axgbe/if_axgbe.c (revision fdafd315)
19c6d6488SAndrew Turner /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37113afc8SEmmanuel Vadot  *
49c6d6488SAndrew Turner  * Copyright (c) 2016,2017 SoftIron Inc.
57113afc8SEmmanuel Vadot  * Copyright (c) 2020 Advanced Micro Devices, Inc.
69c6d6488SAndrew Turner  *
79c6d6488SAndrew Turner  * This software was developed by Andrew Turner under
89c6d6488SAndrew Turner  * the sponsorship of SoftIron Inc.
99c6d6488SAndrew Turner  *
109c6d6488SAndrew Turner  * Redistribution and use in source and binary forms, with or without
119c6d6488SAndrew Turner  * modification, are permitted provided that the following conditions
129c6d6488SAndrew Turner  * are met:
139c6d6488SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
149c6d6488SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
159c6d6488SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
169c6d6488SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
179c6d6488SAndrew Turner  *    documentation and/or other materials provided with the distribution.
189c6d6488SAndrew Turner  *
199c6d6488SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
209c6d6488SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219c6d6488SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229c6d6488SAndrew Turner  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
239c6d6488SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249c6d6488SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259c6d6488SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269c6d6488SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279c6d6488SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289c6d6488SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299c6d6488SAndrew Turner  * SUCH DAMAGE.
309c6d6488SAndrew Turner  */
319c6d6488SAndrew Turner 
329c6d6488SAndrew Turner #include <sys/param.h>
339c6d6488SAndrew Turner #include <sys/systm.h>
349c6d6488SAndrew Turner #include <sys/bus.h>
359c6d6488SAndrew Turner #include <sys/kernel.h>
369c6d6488SAndrew Turner #include <sys/lock.h>
379c6d6488SAndrew Turner #include <sys/malloc.h>
389c6d6488SAndrew Turner #include <sys/module.h>
399c6d6488SAndrew Turner #include <sys/mutex.h>
409c6d6488SAndrew Turner #include <sys/queue.h>
419c6d6488SAndrew Turner #include <sys/rman.h>
429c6d6488SAndrew Turner #include <sys/socket.h>
439c6d6488SAndrew Turner #include <sys/sockio.h>
449c6d6488SAndrew Turner #include <sys/sx.h>
459c6d6488SAndrew Turner #include <sys/taskqueue.h>
469c6d6488SAndrew Turner 
479c6d6488SAndrew Turner #include <net/ethernet.h>
489c6d6488SAndrew Turner #include <net/if.h>
499c6d6488SAndrew Turner #include <net/if_var.h>
509c6d6488SAndrew Turner #include <net/if_media.h>
519c6d6488SAndrew Turner #include <net/if_types.h>
529c6d6488SAndrew Turner 
539c6d6488SAndrew Turner #include <dev/ofw/openfirm.h>
549c6d6488SAndrew Turner #include <dev/ofw/ofw_bus.h>
559c6d6488SAndrew Turner #include <dev/ofw/ofw_bus_subr.h>
569c6d6488SAndrew Turner 
579c6d6488SAndrew Turner #include <machine/bus.h>
589c6d6488SAndrew Turner 
599c6d6488SAndrew Turner #include "miibus_if.h"
609c6d6488SAndrew Turner 
619c6d6488SAndrew Turner #include "xgbe.h"
629c6d6488SAndrew Turner #include "xgbe-common.h"
639c6d6488SAndrew Turner 
649c6d6488SAndrew Turner static device_probe_t	axgbe_probe;
659c6d6488SAndrew Turner static device_attach_t	axgbe_attach;
669c6d6488SAndrew Turner 
679c6d6488SAndrew Turner struct axgbe_softc {
689c6d6488SAndrew Turner 	/* Must be first */
699c6d6488SAndrew Turner 	struct xgbe_prv_data	prv;
709c6d6488SAndrew Turner 
719c6d6488SAndrew Turner 	uint8_t			mac_addr[ETHER_ADDR_LEN];
729c6d6488SAndrew Turner 	struct ifmedia		media;
739c6d6488SAndrew Turner };
749c6d6488SAndrew Turner 
759c6d6488SAndrew Turner static struct ofw_compat_data compat_data[] = {
769c6d6488SAndrew Turner 	{ "amd,xgbe-seattle-v1a",	true },
779c6d6488SAndrew Turner 	{ NULL,				false }
789c6d6488SAndrew Turner };
799c6d6488SAndrew Turner 
809c6d6488SAndrew Turner static struct resource_spec old_phy_spec[] = {
819c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE }, /* Rx/Tx regs */
829c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE }, /* Integration regs */
839c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	2,	RF_ACTIVE }, /* Integration regs */
849c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		0,	RF_ACTIVE }, /* Interrupt */
859c6d6488SAndrew Turner 	{ -1, 0 }
869c6d6488SAndrew Turner };
879c6d6488SAndrew Turner 
889c6d6488SAndrew Turner static struct resource_spec old_mac_spec[] = {
899c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE }, /* MAC regs */
909c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE }, /* PCS regs */
919c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		0,	RF_ACTIVE }, /* Device interrupt */
929c6d6488SAndrew Turner 	/* Per-channel interrupts */
939c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		1,	RF_ACTIVE | RF_OPTIONAL },
949c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		2,	RF_ACTIVE | RF_OPTIONAL },
959c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		3,	RF_ACTIVE | RF_OPTIONAL },
969c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		4,	RF_ACTIVE | RF_OPTIONAL },
979c6d6488SAndrew Turner 	{ -1, 0 }
989c6d6488SAndrew Turner };
999c6d6488SAndrew Turner 
1009c6d6488SAndrew Turner static struct resource_spec mac_spec[] = {
1019c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE }, /* MAC regs */
1029c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE }, /* PCS regs */
1039c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	2,	RF_ACTIVE }, /* Rx/Tx regs */
1049c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	3,	RF_ACTIVE }, /* Integration regs */
1059c6d6488SAndrew Turner 	{ SYS_RES_MEMORY,	4,	RF_ACTIVE }, /* Integration regs */
1069c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		0,	RF_ACTIVE }, /* Device interrupt */
1079c6d6488SAndrew Turner 	/* Per-channel and auto-negotiation interrupts */
1089c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
1099c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		2,	RF_ACTIVE | RF_OPTIONAL },
1109c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		3,	RF_ACTIVE | RF_OPTIONAL },
1119c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		4,	RF_ACTIVE | RF_OPTIONAL },
1129c6d6488SAndrew Turner 	{ SYS_RES_IRQ,		5,	RF_ACTIVE | RF_OPTIONAL },
1139c6d6488SAndrew Turner 	{ -1, 0 }
1149c6d6488SAndrew Turner };
1159c6d6488SAndrew Turner 
1167113afc8SEmmanuel Vadot static struct xgbe_version_data xgbe_v1 = {
1177113afc8SEmmanuel Vadot 	.init_function_ptrs_phy_impl    = xgbe_init_function_ptrs_phy_v1,
1187113afc8SEmmanuel Vadot 	.xpcs_access                    = XGBE_XPCS_ACCESS_V1,
1197113afc8SEmmanuel Vadot 	.tx_max_fifo_size               = 81920,
1207113afc8SEmmanuel Vadot 	.rx_max_fifo_size               = 81920,
1217113afc8SEmmanuel Vadot 	.tx_tstamp_workaround           = 1,
1227113afc8SEmmanuel Vadot };
1237113afc8SEmmanuel Vadot 
1249c6d6488SAndrew Turner MALLOC_DEFINE(M_AXGBE, "axgbe", "axgbe data");
1259c6d6488SAndrew Turner 
1269c6d6488SAndrew Turner static void
axgbe_init(void * p)1279c6d6488SAndrew Turner axgbe_init(void *p)
1289c6d6488SAndrew Turner {
1299c6d6488SAndrew Turner 	struct axgbe_softc *sc;
130402810d3SJustin Hibbits 	if_t ifp;
1319c6d6488SAndrew Turner 
1329c6d6488SAndrew Turner 	sc = p;
1339c6d6488SAndrew Turner 	ifp = sc->prv.netdev;
134402810d3SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1359c6d6488SAndrew Turner 		return;
1369c6d6488SAndrew Turner 
137402810d3SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
1389c6d6488SAndrew Turner }
1399c6d6488SAndrew Turner 
1409c6d6488SAndrew Turner static int
axgbe_ioctl(if_t ifp,unsigned long command,caddr_t data)141402810d3SJustin Hibbits axgbe_ioctl(if_t ifp, unsigned long command, caddr_t data)
1429c6d6488SAndrew Turner {
143402810d3SJustin Hibbits 	struct axgbe_softc *sc = if_getsoftc(ifp);
1449c6d6488SAndrew Turner 	struct ifreq *ifr = (struct ifreq *)data;
1457113afc8SEmmanuel Vadot 	int error = 0;
1469c6d6488SAndrew Turner 
1479c6d6488SAndrew Turner 	switch(command) {
1489c6d6488SAndrew Turner 	case SIOCSIFMTU:
1499c6d6488SAndrew Turner 		if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ETHERMTU_JUMBO)
1509c6d6488SAndrew Turner 			error = EINVAL;
1517113afc8SEmmanuel Vadot 		/* TODO - change it to iflib way */
1529c6d6488SAndrew Turner 		break;
1539c6d6488SAndrew Turner 	case SIOCSIFFLAGS:
1549c6d6488SAndrew Turner 		error = 0;
1559c6d6488SAndrew Turner 		break;
1569c6d6488SAndrew Turner 	case SIOCSIFMEDIA:
1579c6d6488SAndrew Turner 	case SIOCGIFMEDIA:
1589c6d6488SAndrew Turner 		error = ifmedia_ioctl(ifp, ifr, &sc->media, command);
1599c6d6488SAndrew Turner 		break;
1609c6d6488SAndrew Turner 	default:
1619c6d6488SAndrew Turner 		error = ether_ioctl(ifp, command, data);
1629c6d6488SAndrew Turner 		break;
1639c6d6488SAndrew Turner 	}
1649c6d6488SAndrew Turner 
1659c6d6488SAndrew Turner 	return (error);
1669c6d6488SAndrew Turner }
1679c6d6488SAndrew Turner 
1689c6d6488SAndrew Turner static void
axgbe_qflush(if_t ifp)169402810d3SJustin Hibbits axgbe_qflush(if_t ifp)
1709c6d6488SAndrew Turner {
1719c6d6488SAndrew Turner 
1729c6d6488SAndrew Turner 	if_qflush(ifp);
1739c6d6488SAndrew Turner }
1749c6d6488SAndrew Turner 
1759c6d6488SAndrew Turner static int
axgbe_media_change(if_t ifp)176402810d3SJustin Hibbits axgbe_media_change(if_t ifp)
1779c6d6488SAndrew Turner {
1789c6d6488SAndrew Turner 	struct axgbe_softc *sc;
1799c6d6488SAndrew Turner 	int cur_media;
1809c6d6488SAndrew Turner 
181402810d3SJustin Hibbits 	sc = if_getsoftc(ifp);
1829c6d6488SAndrew Turner 
1839c6d6488SAndrew Turner 	sx_xlock(&sc->prv.an_mutex);
1849c6d6488SAndrew Turner 	cur_media = sc->media.ifm_cur->ifm_media;
1859c6d6488SAndrew Turner 
1869c6d6488SAndrew Turner 	switch (IFM_SUBTYPE(cur_media)) {
1879c6d6488SAndrew Turner 	case IFM_10G_KR:
1889c6d6488SAndrew Turner 		sc->prv.phy.speed = SPEED_10000;
1899c6d6488SAndrew Turner 		sc->prv.phy.autoneg = AUTONEG_DISABLE;
1909c6d6488SAndrew Turner 		break;
1919c6d6488SAndrew Turner 	case IFM_2500_KX:
1929c6d6488SAndrew Turner 		sc->prv.phy.speed = SPEED_2500;
1939c6d6488SAndrew Turner 		sc->prv.phy.autoneg = AUTONEG_DISABLE;
1949c6d6488SAndrew Turner 		break;
1959c6d6488SAndrew Turner 	case IFM_1000_KX:
1969c6d6488SAndrew Turner 		sc->prv.phy.speed = SPEED_1000;
1979c6d6488SAndrew Turner 		sc->prv.phy.autoneg = AUTONEG_DISABLE;
1989c6d6488SAndrew Turner 		break;
1999c6d6488SAndrew Turner 	case IFM_AUTO:
2009c6d6488SAndrew Turner 		sc->prv.phy.autoneg = AUTONEG_ENABLE;
2019c6d6488SAndrew Turner 		break;
2029c6d6488SAndrew Turner 	}
2039c6d6488SAndrew Turner 	sx_xunlock(&sc->prv.an_mutex);
2049c6d6488SAndrew Turner 
2059c6d6488SAndrew Turner 	return (-sc->prv.phy_if.phy_config_aneg(&sc->prv));
2069c6d6488SAndrew Turner }
2079c6d6488SAndrew Turner 
2089c6d6488SAndrew Turner static void
axgbe_media_status(if_t ifp,struct ifmediareq * ifmr)209402810d3SJustin Hibbits axgbe_media_status(if_t ifp, struct ifmediareq *ifmr)
2109c6d6488SAndrew Turner {
2119c6d6488SAndrew Turner 	struct axgbe_softc *sc;
2129c6d6488SAndrew Turner 
213402810d3SJustin Hibbits 	sc = if_getsoftc(ifp);
2149c6d6488SAndrew Turner 
2159c6d6488SAndrew Turner 	ifmr->ifm_status = IFM_AVALID;
2169c6d6488SAndrew Turner 	if (!sc->prv.phy.link)
2179c6d6488SAndrew Turner 		return;
2189c6d6488SAndrew Turner 
2199c6d6488SAndrew Turner 	ifmr->ifm_status |= IFM_ACTIVE;
2209c6d6488SAndrew Turner 	ifmr->ifm_active = IFM_ETHER;
2219c6d6488SAndrew Turner 
2229c6d6488SAndrew Turner 	if (sc->prv.phy.duplex == DUPLEX_FULL)
2239c6d6488SAndrew Turner 		ifmr->ifm_active |= IFM_FDX;
2249c6d6488SAndrew Turner 	else
2259c6d6488SAndrew Turner 		ifmr->ifm_active |= IFM_HDX;
2269c6d6488SAndrew Turner 
2279c6d6488SAndrew Turner 	switch (sc->prv.phy.speed) {
2289c6d6488SAndrew Turner 	case SPEED_10000:
2299c6d6488SAndrew Turner 		ifmr->ifm_active |= IFM_10G_KR;
2309c6d6488SAndrew Turner 		break;
2319c6d6488SAndrew Turner 	case SPEED_2500:
2329c6d6488SAndrew Turner 		ifmr->ifm_active |= IFM_2500_KX;
2339c6d6488SAndrew Turner 		break;
2349c6d6488SAndrew Turner 	case SPEED_1000:
2359c6d6488SAndrew Turner 		ifmr->ifm_active |= IFM_1000_KX;
2369c6d6488SAndrew Turner 		break;
2379c6d6488SAndrew Turner 	}
2389c6d6488SAndrew Turner }
2399c6d6488SAndrew Turner 
2409c6d6488SAndrew Turner static uint64_t
axgbe_get_counter(if_t ifp,ift_counter c)241402810d3SJustin Hibbits axgbe_get_counter(if_t ifp, ift_counter c)
2429c6d6488SAndrew Turner {
243402810d3SJustin Hibbits 	struct xgbe_prv_data *pdata = if_getsoftc(ifp);
2449c6d6488SAndrew Turner 	struct xgbe_mmc_stats *pstats = &pdata->mmc_stats;
2459c6d6488SAndrew Turner 
2469c6d6488SAndrew Turner 	DBGPR("-->%s\n", __func__);
2479c6d6488SAndrew Turner 
2489c6d6488SAndrew Turner 	pdata->hw_if.read_mmc_stats(pdata);
2499c6d6488SAndrew Turner 
2509c6d6488SAndrew Turner 	switch(c) {
2519c6d6488SAndrew Turner 	case IFCOUNTER_IPACKETS:
2529c6d6488SAndrew Turner 		return (pstats->rxframecount_gb);
2539c6d6488SAndrew Turner 	case IFCOUNTER_IERRORS:
2549c6d6488SAndrew Turner 		return (pstats->rxframecount_gb -
2559c6d6488SAndrew Turner 		    pstats->rxbroadcastframes_g -
2569c6d6488SAndrew Turner 		    pstats->rxmulticastframes_g -
2579c6d6488SAndrew Turner 		    pstats->rxunicastframes_g);
2589c6d6488SAndrew Turner 	case IFCOUNTER_OPACKETS:
2599c6d6488SAndrew Turner 		return (pstats->txframecount_gb);
2609c6d6488SAndrew Turner 	case IFCOUNTER_OERRORS:
2619c6d6488SAndrew Turner 		return (pstats->txframecount_gb - pstats->txframecount_g);
2629c6d6488SAndrew Turner 	case IFCOUNTER_IBYTES:
2639c6d6488SAndrew Turner 		return (pstats->rxoctetcount_gb);
2649c6d6488SAndrew Turner 	case IFCOUNTER_OBYTES:
2659c6d6488SAndrew Turner 		return (pstats->txoctetcount_gb);
2669c6d6488SAndrew Turner 	default:
2679c6d6488SAndrew Turner 		return (if_get_counter_default(ifp, c));
2689c6d6488SAndrew Turner 	}
2699c6d6488SAndrew Turner }
2709c6d6488SAndrew Turner 
2719c6d6488SAndrew Turner static int
axgbe_probe(device_t dev)2729c6d6488SAndrew Turner axgbe_probe(device_t dev)
2739c6d6488SAndrew Turner {
2749c6d6488SAndrew Turner 
2759c6d6488SAndrew Turner 	if (!ofw_bus_status_okay(dev))
2769c6d6488SAndrew Turner 		return (ENXIO);
2779c6d6488SAndrew Turner 
2789c6d6488SAndrew Turner 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
2799c6d6488SAndrew Turner 		return (ENXIO);
2809c6d6488SAndrew Turner 
2819c6d6488SAndrew Turner 	device_set_desc(dev, "AMD 10 Gigabit Ethernet");
2829c6d6488SAndrew Turner 	return (BUS_PROBE_DEFAULT);
2839c6d6488SAndrew Turner }
2849c6d6488SAndrew Turner 
2859c6d6488SAndrew Turner static int
axgbe_get_optional_prop(device_t dev,phandle_t node,const char * name,int * data,size_t len)2869c6d6488SAndrew Turner axgbe_get_optional_prop(device_t dev, phandle_t node, const char *name,
2879c6d6488SAndrew Turner     int *data, size_t len)
2889c6d6488SAndrew Turner {
2899c6d6488SAndrew Turner 
2909c6d6488SAndrew Turner 	if (!OF_hasprop(node, name))
2919c6d6488SAndrew Turner 		return (-1);
2929c6d6488SAndrew Turner 
2939c6d6488SAndrew Turner 	if (OF_getencprop(node, name, data, len) <= 0) {
2949c6d6488SAndrew Turner 		device_printf(dev,"%s property is invalid\n", name);
2959c6d6488SAndrew Turner 		return (ENXIO);
2969c6d6488SAndrew Turner 	}
2979c6d6488SAndrew Turner 
2989c6d6488SAndrew Turner 	return (0);
2999c6d6488SAndrew Turner }
3009c6d6488SAndrew Turner 
3019c6d6488SAndrew Turner static int
axgbe_attach(device_t dev)3029c6d6488SAndrew Turner axgbe_attach(device_t dev)
3039c6d6488SAndrew Turner {
3049c6d6488SAndrew Turner 	struct axgbe_softc *sc;
305402810d3SJustin Hibbits 	if_t ifp;
3069c6d6488SAndrew Turner 	pcell_t phy_handle;
3079c6d6488SAndrew Turner 	device_t phydev;
3089c6d6488SAndrew Turner 	phandle_t node, phy_node;
3099c6d6488SAndrew Turner 	struct resource *mac_res[11];
3109c6d6488SAndrew Turner 	struct resource *phy_res[4];
3119c6d6488SAndrew Turner 	ssize_t len;
3129c6d6488SAndrew Turner 	int error, i, j;
3139c6d6488SAndrew Turner 
3149c6d6488SAndrew Turner 	sc = device_get_softc(dev);
3159c6d6488SAndrew Turner 
3167113afc8SEmmanuel Vadot 	sc->prv.vdata = &xgbe_v1;
3179c6d6488SAndrew Turner 	node = ofw_bus_get_node(dev);
3189c6d6488SAndrew Turner 	if (OF_getencprop(node, "phy-handle", &phy_handle,
3199c6d6488SAndrew Turner 	    sizeof(phy_handle)) <= 0) {
3209c6d6488SAndrew Turner 		phy_node = node;
3219c6d6488SAndrew Turner 
3229c6d6488SAndrew Turner 		if (bus_alloc_resources(dev, mac_spec, mac_res)) {
3239c6d6488SAndrew Turner 			device_printf(dev,
3249c6d6488SAndrew Turner 			    "could not allocate phy resources\n");
3259c6d6488SAndrew Turner 			return (ENXIO);
3269c6d6488SAndrew Turner 		}
3279c6d6488SAndrew Turner 
3289c6d6488SAndrew Turner 		sc->prv.xgmac_res = mac_res[0];
3299c6d6488SAndrew Turner 		sc->prv.xpcs_res = mac_res[1];
3309c6d6488SAndrew Turner 		sc->prv.rxtx_res = mac_res[2];
3319c6d6488SAndrew Turner 		sc->prv.sir0_res = mac_res[3];
3329c6d6488SAndrew Turner 		sc->prv.sir1_res = mac_res[4];
3339c6d6488SAndrew Turner 
3349c6d6488SAndrew Turner 		sc->prv.dev_irq_res = mac_res[5];
3359c6d6488SAndrew Turner 		sc->prv.per_channel_irq = OF_hasprop(node,
3369c6d6488SAndrew Turner 		    XGBE_DMA_IRQS_PROPERTY);
3379c6d6488SAndrew Turner 		for (i = 0, j = 6; j < nitems(mac_res) - 1 &&
3389c6d6488SAndrew Turner 		    mac_res[j + 1] != NULL; i++, j++) {
3399c6d6488SAndrew Turner 			if (sc->prv.per_channel_irq) {
3409c6d6488SAndrew Turner 				sc->prv.chan_irq_res[i] = mac_res[j];
3419c6d6488SAndrew Turner 			}
3429c6d6488SAndrew Turner 		}
3439c6d6488SAndrew Turner 
3449c6d6488SAndrew Turner 		/* The last entry is the auto-negotiation interrupt */
3459c6d6488SAndrew Turner 		sc->prv.an_irq_res = mac_res[j];
3469c6d6488SAndrew Turner 	} else {
3479c6d6488SAndrew Turner 		phydev = OF_device_from_xref(phy_handle);
3489c6d6488SAndrew Turner 		phy_node = ofw_bus_get_node(phydev);
3499c6d6488SAndrew Turner 
3509c6d6488SAndrew Turner 		if (bus_alloc_resources(phydev, old_phy_spec, phy_res)) {
3519c6d6488SAndrew Turner 			device_printf(dev,
3529c6d6488SAndrew Turner 			    "could not allocate phy resources\n");
3539c6d6488SAndrew Turner 			return (ENXIO);
3549c6d6488SAndrew Turner 		}
3559c6d6488SAndrew Turner 
3569c6d6488SAndrew Turner 		if (bus_alloc_resources(dev, old_mac_spec, mac_res)) {
3579c6d6488SAndrew Turner 			device_printf(dev,
3589c6d6488SAndrew Turner 			    "could not allocate mac resources\n");
3599c6d6488SAndrew Turner 			return (ENXIO);
3609c6d6488SAndrew Turner 		}
3619c6d6488SAndrew Turner 
3629c6d6488SAndrew Turner 		sc->prv.rxtx_res = phy_res[0];
3639c6d6488SAndrew Turner 		sc->prv.sir0_res = phy_res[1];
3649c6d6488SAndrew Turner 		sc->prv.sir1_res = phy_res[2];
3659c6d6488SAndrew Turner 		sc->prv.an_irq_res = phy_res[3];
3669c6d6488SAndrew Turner 
3679c6d6488SAndrew Turner 		sc->prv.xgmac_res = mac_res[0];
3689c6d6488SAndrew Turner 		sc->prv.xpcs_res = mac_res[1];
3699c6d6488SAndrew Turner 		sc->prv.dev_irq_res = mac_res[2];
3709c6d6488SAndrew Turner 		sc->prv.per_channel_irq = OF_hasprop(node,
3719c6d6488SAndrew Turner 		    XGBE_DMA_IRQS_PROPERTY);
3729c6d6488SAndrew Turner 		if (sc->prv.per_channel_irq) {
3739c6d6488SAndrew Turner 			for (i = 0, j = 3; i < nitems(sc->prv.chan_irq_res) &&
3749c6d6488SAndrew Turner 			    mac_res[j] != NULL; i++, j++) {
3759c6d6488SAndrew Turner 				sc->prv.chan_irq_res[i] = mac_res[j];
3769c6d6488SAndrew Turner 			}
3779c6d6488SAndrew Turner 		}
3789c6d6488SAndrew Turner 	}
3799c6d6488SAndrew Turner 
3809c6d6488SAndrew Turner 	if ((len = OF_getproplen(node, "mac-address")) < 0) {
3819c6d6488SAndrew Turner 		device_printf(dev, "No mac-address property\n");
3829c6d6488SAndrew Turner 		return (EINVAL);
3839c6d6488SAndrew Turner 	}
3849c6d6488SAndrew Turner 
3859c6d6488SAndrew Turner 	if (len != ETHER_ADDR_LEN)
3869c6d6488SAndrew Turner 		return (EINVAL);
3879c6d6488SAndrew Turner 
3889c6d6488SAndrew Turner 	OF_getprop(node, "mac-address", sc->mac_addr, ETHER_ADDR_LEN);
3899c6d6488SAndrew Turner 
3909c6d6488SAndrew Turner 	sc->prv.netdev = ifp = if_alloc(IFT_ETHER);
3919c6d6488SAndrew Turner 	if (ifp == NULL) {
3929c6d6488SAndrew Turner 		device_printf(dev, "Cannot alloc ifnet\n");
3939c6d6488SAndrew Turner 		return (ENXIO);
3949c6d6488SAndrew Turner 	}
3959c6d6488SAndrew Turner 
3969c6d6488SAndrew Turner 	sc->prv.dev = dev;
3979c6d6488SAndrew Turner 	sc->prv.dmat = bus_get_dma_tag(dev);
3989c6d6488SAndrew Turner 	sc->prv.phy.advertising = ADVERTISED_10000baseKR_Full |
3999c6d6488SAndrew Turner 	    ADVERTISED_1000baseKX_Full;
4009c6d6488SAndrew Turner 
4017113afc8SEmmanuel Vadot 
4029c6d6488SAndrew Turner 	/*
4039c6d6488SAndrew Turner 	 * Read the needed properties from the phy node.
4049c6d6488SAndrew Turner 	 */
4059c6d6488SAndrew Turner 
4069c6d6488SAndrew Turner 	/* This is documented as optional, but Linux requires it */
4079c6d6488SAndrew Turner 	if (OF_getencprop(phy_node, XGBE_SPEEDSET_PROPERTY, &sc->prv.speed_set,
4089c6d6488SAndrew Turner 	    sizeof(sc->prv.speed_set)) <= 0) {
4099c6d6488SAndrew Turner 		device_printf(dev, "%s property is missing\n",
4109c6d6488SAndrew Turner 		    XGBE_SPEEDSET_PROPERTY);
4119c6d6488SAndrew Turner 		return (EINVAL);
4129c6d6488SAndrew Turner 	}
4139c6d6488SAndrew Turner 
4149c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_BLWC_PROPERTY,
4159c6d6488SAndrew Turner 	    sc->prv.serdes_blwc, sizeof(sc->prv.serdes_blwc));
4169c6d6488SAndrew Turner 	if (error > 0) {
4179c6d6488SAndrew Turner 		return (error);
4189c6d6488SAndrew Turner 	} else if (error < 0) {
4199c6d6488SAndrew Turner 		sc->prv.serdes_blwc[0] = XGBE_SPEED_1000_BLWC;
4209c6d6488SAndrew Turner 		sc->prv.serdes_blwc[1] = XGBE_SPEED_2500_BLWC;
4219c6d6488SAndrew Turner 		sc->prv.serdes_blwc[2] = XGBE_SPEED_10000_BLWC;
4229c6d6488SAndrew Turner 	}
4239c6d6488SAndrew Turner 
4249c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_CDR_RATE_PROPERTY,
4259c6d6488SAndrew Turner 	    sc->prv.serdes_cdr_rate, sizeof(sc->prv.serdes_cdr_rate));
4269c6d6488SAndrew Turner 	if (error > 0) {
4279c6d6488SAndrew Turner 		return (error);
4289c6d6488SAndrew Turner 	} else if (error < 0) {
4299c6d6488SAndrew Turner 		sc->prv.serdes_cdr_rate[0] = XGBE_SPEED_1000_CDR;
4309c6d6488SAndrew Turner 		sc->prv.serdes_cdr_rate[1] = XGBE_SPEED_2500_CDR;
4319c6d6488SAndrew Turner 		sc->prv.serdes_cdr_rate[2] = XGBE_SPEED_10000_CDR;
4329c6d6488SAndrew Turner 	}
4339c6d6488SAndrew Turner 
4349c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_PQ_SKEW_PROPERTY,
4359c6d6488SAndrew Turner 	    sc->prv.serdes_pq_skew, sizeof(sc->prv.serdes_pq_skew));
4369c6d6488SAndrew Turner 	if (error > 0) {
4379c6d6488SAndrew Turner 		return (error);
4389c6d6488SAndrew Turner 	} else if (error < 0) {
4399c6d6488SAndrew Turner 		sc->prv.serdes_pq_skew[0] = XGBE_SPEED_1000_PQ;
4409c6d6488SAndrew Turner 		sc->prv.serdes_pq_skew[1] = XGBE_SPEED_2500_PQ;
4419c6d6488SAndrew Turner 		sc->prv.serdes_pq_skew[2] = XGBE_SPEED_10000_PQ;
4429c6d6488SAndrew Turner 	}
4439c6d6488SAndrew Turner 
4449c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_TX_AMP_PROPERTY,
4459c6d6488SAndrew Turner 	    sc->prv.serdes_tx_amp, sizeof(sc->prv.serdes_tx_amp));
4469c6d6488SAndrew Turner 	if (error > 0) {
4479c6d6488SAndrew Turner 		return (error);
4489c6d6488SAndrew Turner 	} else if (error < 0) {
4499c6d6488SAndrew Turner 		sc->prv.serdes_tx_amp[0] = XGBE_SPEED_1000_TXAMP;
4509c6d6488SAndrew Turner 		sc->prv.serdes_tx_amp[1] = XGBE_SPEED_2500_TXAMP;
4519c6d6488SAndrew Turner 		sc->prv.serdes_tx_amp[2] = XGBE_SPEED_10000_TXAMP;
4529c6d6488SAndrew Turner 	}
4539c6d6488SAndrew Turner 
4549c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_DFE_CFG_PROPERTY,
4559c6d6488SAndrew Turner 	    sc->prv.serdes_dfe_tap_cfg, sizeof(sc->prv.serdes_dfe_tap_cfg));
4569c6d6488SAndrew Turner 	if (error > 0) {
4579c6d6488SAndrew Turner 		return (error);
4589c6d6488SAndrew Turner 	} else if (error < 0) {
4599c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_cfg[0] = XGBE_SPEED_1000_DFE_TAP_CONFIG;
4609c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_cfg[1] = XGBE_SPEED_2500_DFE_TAP_CONFIG;
4619c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_cfg[2] = XGBE_SPEED_10000_DFE_TAP_CONFIG;
4629c6d6488SAndrew Turner 	}
4639c6d6488SAndrew Turner 
4649c6d6488SAndrew Turner 	error = axgbe_get_optional_prop(dev, phy_node, XGBE_DFE_ENA_PROPERTY,
4659c6d6488SAndrew Turner 	    sc->prv.serdes_dfe_tap_ena, sizeof(sc->prv.serdes_dfe_tap_ena));
4669c6d6488SAndrew Turner 	if (error > 0) {
4679c6d6488SAndrew Turner 		return (error);
4689c6d6488SAndrew Turner 	} else if (error < 0) {
4699c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_ena[0] = XGBE_SPEED_1000_DFE_TAP_ENABLE;
4709c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_ena[1] = XGBE_SPEED_2500_DFE_TAP_ENABLE;
4719c6d6488SAndrew Turner 		sc->prv.serdes_dfe_tap_ena[2] = XGBE_SPEED_10000_DFE_TAP_ENABLE;
4729c6d6488SAndrew Turner 	}
4739c6d6488SAndrew Turner 
4749c6d6488SAndrew Turner 	/* Check if the NIC is DMA coherent */
4759c6d6488SAndrew Turner 	sc->prv.coherent = OF_hasprop(node, "dma-coherent");
4769c6d6488SAndrew Turner 	if (sc->prv.coherent) {
4777113afc8SEmmanuel Vadot 		sc->prv.arcr = XGBE_DMA_OS_ARCR;
4787113afc8SEmmanuel Vadot 		sc->prv.awcr = XGBE_DMA_OS_AWCR;
4799c6d6488SAndrew Turner 	} else {
4807113afc8SEmmanuel Vadot 		sc->prv.arcr = XGBE_DMA_SYS_ARCR;
4817113afc8SEmmanuel Vadot 		sc->prv.awcr = XGBE_DMA_SYS_AWCR;
4829c6d6488SAndrew Turner 	}
4839c6d6488SAndrew Turner 
4849c6d6488SAndrew Turner 	/* Create the lock & workqueues */
4859c6d6488SAndrew Turner 	spin_lock_init(&sc->prv.xpcs_lock);
4869c6d6488SAndrew Turner 	sc->prv.dev_workqueue = taskqueue_create("axgbe", M_WAITOK,
4879c6d6488SAndrew Turner 	    taskqueue_thread_enqueue, &sc->prv.dev_workqueue);
4889c6d6488SAndrew Turner 	taskqueue_start_threads(&sc->prv.dev_workqueue, 1, PI_NET,
4899c6d6488SAndrew Turner 	    "axgbe taskq");
4909c6d6488SAndrew Turner 
4919c6d6488SAndrew Turner 	/* Set the needed pointers */
4929c6d6488SAndrew Turner 	xgbe_init_function_ptrs_phy(&sc->prv.phy_if);
4939c6d6488SAndrew Turner 	xgbe_init_function_ptrs_dev(&sc->prv.hw_if);
4949c6d6488SAndrew Turner 	xgbe_init_function_ptrs_desc(&sc->prv.desc_if);
4957113afc8SEmmanuel Vadot 	sc->prv.vdata->init_function_ptrs_phy_impl(&sc->prv.phy_if);
4969c6d6488SAndrew Turner 
4979c6d6488SAndrew Turner 	/* Reset the hardware */
4989c6d6488SAndrew Turner 	sc->prv.hw_if.exit(&sc->prv);
4999c6d6488SAndrew Turner 
5009c6d6488SAndrew Turner 	/* Read the hardware features */
5019c6d6488SAndrew Turner 	xgbe_get_all_hw_features(&sc->prv);
5029c6d6488SAndrew Turner 
5039c6d6488SAndrew Turner 	/* Set default values */
5049c6d6488SAndrew Turner 	sc->prv.tx_desc_count = XGBE_TX_DESC_CNT;
5059c6d6488SAndrew Turner 	sc->prv.tx_sf_mode = MTL_TSF_ENABLE;
5069c6d6488SAndrew Turner 	sc->prv.tx_threshold = MTL_TX_THRESHOLD_64;
5079c6d6488SAndrew Turner 	sc->prv.tx_osp_mode = DMA_OSP_ENABLE;
5089c6d6488SAndrew Turner 	sc->prv.rx_desc_count = XGBE_RX_DESC_CNT;
5099c6d6488SAndrew Turner 	sc->prv.rx_sf_mode = MTL_RSF_DISABLE;
5109c6d6488SAndrew Turner 	sc->prv.rx_threshold = MTL_RX_THRESHOLD_64;
5117113afc8SEmmanuel Vadot 	sc->prv.pbl = DMA_PBL_128;
5129c6d6488SAndrew Turner 	sc->prv.pause_autoneg = 1;
5139c6d6488SAndrew Turner 	sc->prv.tx_pause = 1;
5149c6d6488SAndrew Turner 	sc->prv.rx_pause = 1;
5159c6d6488SAndrew Turner 	sc->prv.phy_speed = SPEED_UNKNOWN;
5169c6d6488SAndrew Turner 	sc->prv.power_down = 0;
5179c6d6488SAndrew Turner 
5189c6d6488SAndrew Turner 	/* TODO: Limit to min(ncpus, hw rings) */
5199c6d6488SAndrew Turner 	sc->prv.tx_ring_count = 1;
5209c6d6488SAndrew Turner 	sc->prv.tx_q_count = 1;
5219c6d6488SAndrew Turner 	sc->prv.rx_ring_count = 1;
5229c6d6488SAndrew Turner 	sc->prv.rx_q_count = sc->prv.hw_feat.rx_q_cnt;
5239c6d6488SAndrew Turner 
5249c6d6488SAndrew Turner 	/* Init the PHY */
5259c6d6488SAndrew Turner 	sc->prv.phy_if.phy_init(&sc->prv);
5269c6d6488SAndrew Turner 
5279c6d6488SAndrew Turner 	/* Set the coalescing */
5289c6d6488SAndrew Turner 	xgbe_init_rx_coalesce(&sc->prv);
5299c6d6488SAndrew Turner 	xgbe_init_tx_coalesce(&sc->prv);
5309c6d6488SAndrew Turner 
5319c6d6488SAndrew Turner 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
532402810d3SJustin Hibbits 	if_setinitfn(ifp, axgbe_init);
533402810d3SJustin Hibbits         if_setsoftc(ifp, sc);
534402810d3SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
535402810d3SJustin Hibbits 	if_setioctlfn(ifp, axgbe_ioctl);
5367113afc8SEmmanuel Vadot 	/* TODO - change it to iflib way */
537402810d3SJustin Hibbits 	if_setqflushfn(ifp, axgbe_qflush);
538402810d3SJustin Hibbits 	if_setgetcounterfn(ifp, axgbe_get_counter);
5399c6d6488SAndrew Turner 
5409c6d6488SAndrew Turner 	/* TODO: Support HW offload */
541402810d3SJustin Hibbits 	if_setcapabilities(ifp, 0);
542402810d3SJustin Hibbits 	if_setcapenable(ifp, 0);
543402810d3SJustin Hibbits 	if_sethwassist(ifp, 0);
5449c6d6488SAndrew Turner 
5459c6d6488SAndrew Turner 	ether_ifattach(ifp, sc->mac_addr);
5469c6d6488SAndrew Turner 
5479c6d6488SAndrew Turner 	ifmedia_init(&sc->media, IFM_IMASK, axgbe_media_change,
5489c6d6488SAndrew Turner 	    axgbe_media_status);
5499c6d6488SAndrew Turner #ifdef notyet
5509c6d6488SAndrew Turner 	ifmedia_add(&sc->media, IFM_ETHER | IFM_10G_KR, 0, NULL);
5519c6d6488SAndrew Turner #endif
5529c6d6488SAndrew Turner 	ifmedia_add(&sc->media, IFM_ETHER | IFM_1000_KX, 0, NULL);
5539c6d6488SAndrew Turner 	ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL);
5549c6d6488SAndrew Turner 	ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO);
5559c6d6488SAndrew Turner 
5569c6d6488SAndrew Turner 	set_bit(XGBE_DOWN, &sc->prv.dev_state);
5579c6d6488SAndrew Turner 
5587113afc8SEmmanuel Vadot 	/* TODO - change it to iflib way */
5599c6d6488SAndrew Turner 	return (0);
5609c6d6488SAndrew Turner }
5619c6d6488SAndrew Turner 
5629c6d6488SAndrew Turner static device_method_t axgbe_methods[] = {
5639c6d6488SAndrew Turner 	/* Device interface */
5649c6d6488SAndrew Turner 	DEVMETHOD(device_probe,		axgbe_probe),
5659c6d6488SAndrew Turner 	DEVMETHOD(device_attach,	axgbe_attach),
5667113afc8SEmmanuel Vadot 
5679c6d6488SAndrew Turner 	{ 0, 0 }
5689c6d6488SAndrew Turner };
5699c6d6488SAndrew Turner 
5709c6d6488SAndrew Turner DEFINE_CLASS_0(axgbe, axgbe_driver, axgbe_methods,
5719c6d6488SAndrew Turner     sizeof(struct axgbe_softc));
57295d60710SJohn Baldwin DRIVER_MODULE(axa, simplebus, axgbe_driver, 0, 0);
5737113afc8SEmmanuel Vadot 
5749c6d6488SAndrew Turner 
5759c6d6488SAndrew Turner static struct ofw_compat_data phy_compat_data[] = {
5769c6d6488SAndrew Turner 	{ "amd,xgbe-phy-seattle-v1a",	true },
5779c6d6488SAndrew Turner 	{ NULL,				false }
5789c6d6488SAndrew Turner };
5799c6d6488SAndrew Turner 
5809c6d6488SAndrew Turner static int
axgbephy_probe(device_t dev)5819c6d6488SAndrew Turner axgbephy_probe(device_t dev)
5829c6d6488SAndrew Turner {
5839c6d6488SAndrew Turner 
5849c6d6488SAndrew Turner 	if (!ofw_bus_status_okay(dev))
5859c6d6488SAndrew Turner 		return (ENXIO);
5869c6d6488SAndrew Turner 
5879c6d6488SAndrew Turner 	if (!ofw_bus_search_compatible(dev, phy_compat_data)->ocd_data)
5889c6d6488SAndrew Turner 		return (ENXIO);
5899c6d6488SAndrew Turner 
5909c6d6488SAndrew Turner 	device_set_desc(dev, "AMD 10 Gigabit Ethernet");
5919c6d6488SAndrew Turner 	return (BUS_PROBE_DEFAULT);
5929c6d6488SAndrew Turner }
5939c6d6488SAndrew Turner 
5949c6d6488SAndrew Turner static int
axgbephy_attach(device_t dev)5959c6d6488SAndrew Turner axgbephy_attach(device_t dev)
5969c6d6488SAndrew Turner {
5979c6d6488SAndrew Turner 	phandle_t node;
5989c6d6488SAndrew Turner 
5999c6d6488SAndrew Turner 	node = ofw_bus_get_node(dev);
6009c6d6488SAndrew Turner 	OF_device_register_xref(OF_xref_from_node(node), dev);
6019c6d6488SAndrew Turner 
6029c6d6488SAndrew Turner 	return (0);
6039c6d6488SAndrew Turner }
6049c6d6488SAndrew Turner 
6059c6d6488SAndrew Turner static device_method_t axgbephy_methods[] = {
6069c6d6488SAndrew Turner 	/* Device interface */
6079c6d6488SAndrew Turner 	DEVMETHOD(device_probe,		axgbephy_probe),
6089c6d6488SAndrew Turner 	DEVMETHOD(device_attach,	axgbephy_attach),
6097113afc8SEmmanuel Vadot 
6109c6d6488SAndrew Turner 	{ 0, 0 }
6119c6d6488SAndrew Turner };
6129c6d6488SAndrew Turner 
6139c6d6488SAndrew Turner DEFINE_CLASS_0(axgbephy, axgbephy_driver, axgbephy_methods, 0);
61495d60710SJohn Baldwin EARLY_DRIVER_MODULE(axgbephy, simplebus, axgbephy_driver,
6159c6d6488SAndrew Turner     0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
616