xref: /freebsd-src/sys/dev/vnic/thunder_bgx.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
13c0086b8SZbigniew Bodek /*
23c0086b8SZbigniew Bodek  * Copyright (C) 2015 Cavium Inc.
33c0086b8SZbigniew Bodek  * All rights reserved.
43c0086b8SZbigniew Bodek  *
53c0086b8SZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
63c0086b8SZbigniew Bodek  * modification, are permitted provided that the following conditions
73c0086b8SZbigniew Bodek  * are met:
83c0086b8SZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
93c0086b8SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
103c0086b8SZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
113c0086b8SZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
123c0086b8SZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
133c0086b8SZbigniew Bodek  *
143c0086b8SZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
153c0086b8SZbigniew Bodek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
163c0086b8SZbigniew Bodek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173c0086b8SZbigniew Bodek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
183c0086b8SZbigniew Bodek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
193c0086b8SZbigniew Bodek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
203c0086b8SZbigniew Bodek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
213c0086b8SZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223c0086b8SZbigniew Bodek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233c0086b8SZbigniew Bodek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
243c0086b8SZbigniew Bodek  * SUCH DAMAGE.
253c0086b8SZbigniew Bodek  *
263c0086b8SZbigniew Bodek  */
272306b72aSZbigniew Bodek #include "opt_platform.h"
283c0086b8SZbigniew Bodek 
292306b72aSZbigniew Bodek #include <sys/param.h>
302306b72aSZbigniew Bodek #include <sys/systm.h>
312306b72aSZbigniew Bodek #include <sys/bitset.h>
322306b72aSZbigniew Bodek #include <sys/bitstring.h>
332306b72aSZbigniew Bodek #include <sys/bus.h>
342306b72aSZbigniew Bodek #include <sys/endian.h>
352306b72aSZbigniew Bodek #include <sys/kernel.h>
362306b72aSZbigniew Bodek #include <sys/malloc.h>
372306b72aSZbigniew Bodek #include <sys/module.h>
382306b72aSZbigniew Bodek #include <sys/rman.h>
392306b72aSZbigniew Bodek #include <sys/pciio.h>
402306b72aSZbigniew Bodek #include <sys/pcpu.h>
412306b72aSZbigniew Bodek #include <sys/proc.h>
422306b72aSZbigniew Bodek #include <sys/socket.h>
432306b72aSZbigniew Bodek #include <sys/sockio.h>
442306b72aSZbigniew Bodek #include <sys/cpuset.h>
452306b72aSZbigniew Bodek #include <sys/lock.h>
462306b72aSZbigniew Bodek #include <sys/mutex.h>
472306b72aSZbigniew Bodek 
482306b72aSZbigniew Bodek #include <net/ethernet.h>
492306b72aSZbigniew Bodek #include <net/if.h>
502306b72aSZbigniew Bodek #include <net/if_media.h>
512306b72aSZbigniew Bodek 
522306b72aSZbigniew Bodek #include <machine/bus.h>
532306b72aSZbigniew Bodek 
542306b72aSZbigniew Bodek #include <dev/pci/pcireg.h>
552306b72aSZbigniew Bodek #include <dev/pci/pcivar.h>
562306b72aSZbigniew Bodek 
572306b72aSZbigniew Bodek #include "thunder_bgx.h"
582306b72aSZbigniew Bodek #include "thunder_bgx_var.h"
593c0086b8SZbigniew Bodek #include "nic_reg.h"
603c0086b8SZbigniew Bodek #include "nic.h"
612306b72aSZbigniew Bodek 
622306b72aSZbigniew Bodek #include "lmac_if.h"
632306b72aSZbigniew Bodek 
642306b72aSZbigniew Bodek #define	THUNDER_BGX_DEVSTR	"ThunderX BGX Ethernet I/O Interface"
652306b72aSZbigniew Bodek 
66be624ad4SZbigniew Bodek MALLOC_DEFINE(M_BGX, "thunder_bgx", "ThunderX BGX dynamic memory");
672306b72aSZbigniew Bodek 
682306b72aSZbigniew Bodek #define BGX_NODE_ID_MASK	0x1
692306b72aSZbigniew Bodek #define BGX_NODE_ID_SHIFT	24
703c0086b8SZbigniew Bodek 
713c0086b8SZbigniew Bodek #define DRV_NAME	"thunder-BGX"
723c0086b8SZbigniew Bodek #define DRV_VERSION	"1.0"
733c0086b8SZbigniew Bodek 
742306b72aSZbigniew Bodek static int bgx_init_phy(struct bgx *);
753c0086b8SZbigniew Bodek 
763c0086b8SZbigniew Bodek static struct bgx *bgx_vnic[MAX_BGX_THUNDER];
772306b72aSZbigniew Bodek static int lmac_count __unused; /* Total no of LMACs in system */
783c0086b8SZbigniew Bodek 
793c0086b8SZbigniew Bodek static int bgx_xaui_check_link(struct lmac *lmac);
802306b72aSZbigniew Bodek static void bgx_get_qlm_mode(struct bgx *);
812306b72aSZbigniew Bodek static void bgx_init_hw(struct bgx *);
822306b72aSZbigniew Bodek static int bgx_lmac_enable(struct bgx *, uint8_t);
832306b72aSZbigniew Bodek static void bgx_lmac_disable(struct bgx *, uint8_t);
843c0086b8SZbigniew Bodek 
852306b72aSZbigniew Bodek static int thunder_bgx_probe(device_t);
862306b72aSZbigniew Bodek static int thunder_bgx_attach(device_t);
872306b72aSZbigniew Bodek static int thunder_bgx_detach(device_t);
882306b72aSZbigniew Bodek 
892306b72aSZbigniew Bodek static device_method_t thunder_bgx_methods[] = {
902306b72aSZbigniew Bodek 	/* Device interface */
912306b72aSZbigniew Bodek 	DEVMETHOD(device_probe,		thunder_bgx_probe),
922306b72aSZbigniew Bodek 	DEVMETHOD(device_attach,	thunder_bgx_attach),
932306b72aSZbigniew Bodek 	DEVMETHOD(device_detach,	thunder_bgx_detach),
942306b72aSZbigniew Bodek 
952306b72aSZbigniew Bodek 	DEVMETHOD_END,
963c0086b8SZbigniew Bodek };
973c0086b8SZbigniew Bodek 
982306b72aSZbigniew Bodek static driver_t thunder_bgx_driver = {
992306b72aSZbigniew Bodek 	"bgx",
1002306b72aSZbigniew Bodek 	thunder_bgx_methods,
1012306b72aSZbigniew Bodek 	sizeof(struct lmac),
1022306b72aSZbigniew Bodek };
1033c0086b8SZbigniew Bodek 
104*94412ad7SJohn Baldwin DRIVER_MODULE(thunder_bgx, pci, thunder_bgx_driver, 0, 0);
105f4aafb9eSWojciech Macek MODULE_VERSION(thunder_bgx, 1);
1062306b72aSZbigniew Bodek MODULE_DEPEND(thunder_bgx, pci, 1, 1, 1);
1072306b72aSZbigniew Bodek MODULE_DEPEND(thunder_bgx, ether, 1, 1, 1);
108f4aafb9eSWojciech Macek MODULE_DEPEND(thunder_bgx, thunder_mdio, 1, 1, 1);
1092306b72aSZbigniew Bodek 
1102306b72aSZbigniew Bodek static int
thunder_bgx_probe(device_t dev)1112306b72aSZbigniew Bodek thunder_bgx_probe(device_t dev)
1122306b72aSZbigniew Bodek {
1132306b72aSZbigniew Bodek 	uint16_t vendor_id;
1142306b72aSZbigniew Bodek 	uint16_t device_id;
1152306b72aSZbigniew Bodek 
1162306b72aSZbigniew Bodek 	vendor_id = pci_get_vendor(dev);
1172306b72aSZbigniew Bodek 	device_id = pci_get_device(dev);
1182306b72aSZbigniew Bodek 
1192306b72aSZbigniew Bodek 	if (vendor_id == PCI_VENDOR_ID_CAVIUM &&
1202306b72aSZbigniew Bodek 	    device_id == PCI_DEVICE_ID_THUNDER_BGX) {
1212306b72aSZbigniew Bodek 		device_set_desc(dev, THUNDER_BGX_DEVSTR);
1222306b72aSZbigniew Bodek 		return (BUS_PROBE_DEFAULT);
1232306b72aSZbigniew Bodek 	}
1242306b72aSZbigniew Bodek 
1252306b72aSZbigniew Bodek 	return (ENXIO);
1262306b72aSZbigniew Bodek }
1272306b72aSZbigniew Bodek 
1282306b72aSZbigniew Bodek static int
thunder_bgx_attach(device_t dev)1292306b72aSZbigniew Bodek thunder_bgx_attach(device_t dev)
1302306b72aSZbigniew Bodek {
1312306b72aSZbigniew Bodek 	struct bgx *bgx;
1327056927eSWojciech Macek 	uint8_t lmacid;
1332306b72aSZbigniew Bodek 	int err;
1342306b72aSZbigniew Bodek 	int rid;
1357056927eSWojciech Macek 	struct lmac *lmac;
1362306b72aSZbigniew Bodek 
1372306b72aSZbigniew Bodek 	bgx = malloc(sizeof(*bgx), M_BGX, (M_WAITOK | M_ZERO));
1382306b72aSZbigniew Bodek 	bgx->dev = dev;
1397056927eSWojciech Macek 
1407056927eSWojciech Macek 	lmac = device_get_softc(dev);
1417056927eSWojciech Macek 	lmac->bgx = bgx;
1422306b72aSZbigniew Bodek 	/* Enable bus mastering */
1432306b72aSZbigniew Bodek 	pci_enable_busmaster(dev);
1442306b72aSZbigniew Bodek 	/* Allocate resources - configuration registers */
1452306b72aSZbigniew Bodek 	rid = PCIR_BAR(PCI_CFG_REG_BAR_NUM);
1462306b72aSZbigniew Bodek 	bgx->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1472306b72aSZbigniew Bodek 	    RF_ACTIVE);
1482306b72aSZbigniew Bodek 	if (bgx->reg_base == NULL) {
1492306b72aSZbigniew Bodek 		device_printf(dev, "Could not allocate CSR memory space\n");
1502306b72aSZbigniew Bodek 		err = ENXIO;
1512306b72aSZbigniew Bodek 		goto err_disable_device;
1522306b72aSZbigniew Bodek 	}
1532306b72aSZbigniew Bodek 
1542306b72aSZbigniew Bodek 	bgx->bgx_id = (rman_get_start(bgx->reg_base) >> BGX_NODE_ID_SHIFT) &
1552306b72aSZbigniew Bodek 	    BGX_NODE_ID_MASK;
1562306b72aSZbigniew Bodek 	bgx->bgx_id += nic_get_node_id(bgx->reg_base) * MAX_BGX_PER_CN88XX;
1572306b72aSZbigniew Bodek 
1582306b72aSZbigniew Bodek 	bgx_vnic[bgx->bgx_id] = bgx;
1592306b72aSZbigniew Bodek 	bgx_get_qlm_mode(bgx);
1602306b72aSZbigniew Bodek 
1612306b72aSZbigniew Bodek 	err = bgx_init_phy(bgx);
1622306b72aSZbigniew Bodek 	if (err != 0)
1632306b72aSZbigniew Bodek 		goto err_free_res;
1642306b72aSZbigniew Bodek 
1652306b72aSZbigniew Bodek 	bgx_init_hw(bgx);
1662306b72aSZbigniew Bodek 
1672306b72aSZbigniew Bodek 	/* Enable all LMACs */
1687056927eSWojciech Macek 	for (lmacid = 0; lmacid < bgx->lmac_count; lmacid++) {
1697056927eSWojciech Macek 		err = bgx_lmac_enable(bgx, lmacid);
1702306b72aSZbigniew Bodek 		if (err) {
1712306b72aSZbigniew Bodek 			device_printf(dev, "BGX%d failed to enable lmac%d\n",
1727056927eSWojciech Macek 				bgx->bgx_id, lmacid);
1732306b72aSZbigniew Bodek 			goto err_free_res;
1742306b72aSZbigniew Bodek 		}
1752306b72aSZbigniew Bodek 	}
1762306b72aSZbigniew Bodek 
1772306b72aSZbigniew Bodek 	return (0);
1782306b72aSZbigniew Bodek 
1792306b72aSZbigniew Bodek err_free_res:
1802306b72aSZbigniew Bodek 	bgx_vnic[bgx->bgx_id] = NULL;
1812306b72aSZbigniew Bodek 	bus_release_resource(dev, SYS_RES_MEMORY,
1822306b72aSZbigniew Bodek 	    rman_get_rid(bgx->reg_base), bgx->reg_base);
1832306b72aSZbigniew Bodek err_disable_device:
1842306b72aSZbigniew Bodek 	free(bgx, M_BGX);
1852306b72aSZbigniew Bodek 	pci_disable_busmaster(dev);
1862306b72aSZbigniew Bodek 
1872306b72aSZbigniew Bodek 	return (err);
1882306b72aSZbigniew Bodek }
1892306b72aSZbigniew Bodek 
1902306b72aSZbigniew Bodek static int
thunder_bgx_detach(device_t dev)1912306b72aSZbigniew Bodek thunder_bgx_detach(device_t dev)
1922306b72aSZbigniew Bodek {
1932306b72aSZbigniew Bodek 	struct lmac *lmac;
1942306b72aSZbigniew Bodek 	struct bgx *bgx;
1952306b72aSZbigniew Bodek 	uint8_t lmacid;
1962306b72aSZbigniew Bodek 
1972306b72aSZbigniew Bodek 	lmac = device_get_softc(dev);
1982306b72aSZbigniew Bodek 	bgx = lmac->bgx;
1992306b72aSZbigniew Bodek 	/* Disable all LMACs */
2002306b72aSZbigniew Bodek 	for (lmacid = 0; lmacid < bgx->lmac_count; lmacid++)
2012306b72aSZbigniew Bodek 		bgx_lmac_disable(bgx, lmacid);
2022306b72aSZbigniew Bodek 
2037056927eSWojciech Macek 	bgx_vnic[bgx->bgx_id] = NULL;
2047056927eSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY,
2057056927eSWojciech Macek 	    rman_get_rid(bgx->reg_base), bgx->reg_base);
2067056927eSWojciech Macek 	free(bgx, M_BGX);
2077056927eSWojciech Macek 	pci_disable_busmaster(dev);
2087056927eSWojciech Macek 
2092306b72aSZbigniew Bodek 	return (0);
2102306b72aSZbigniew Bodek }
2113c0086b8SZbigniew Bodek 
2123c0086b8SZbigniew Bodek /* Register read/write APIs */
2132306b72aSZbigniew Bodek static uint64_t
bgx_reg_read(struct bgx * bgx,uint8_t lmac,uint64_t offset)2142306b72aSZbigniew Bodek bgx_reg_read(struct bgx *bgx, uint8_t lmac, uint64_t offset)
2153c0086b8SZbigniew Bodek {
2162306b72aSZbigniew Bodek 	bus_space_handle_t addr;
2173c0086b8SZbigniew Bodek 
2182306b72aSZbigniew Bodek 	addr = ((uint32_t)lmac << 20) + offset;
2192306b72aSZbigniew Bodek 
2202306b72aSZbigniew Bodek 	return (bus_read_8(bgx->reg_base, addr));
2213c0086b8SZbigniew Bodek }
2223c0086b8SZbigniew Bodek 
2232306b72aSZbigniew Bodek static void
bgx_reg_write(struct bgx * bgx,uint8_t lmac,uint64_t offset,uint64_t val)2242306b72aSZbigniew Bodek bgx_reg_write(struct bgx *bgx, uint8_t lmac, uint64_t offset, uint64_t val)
2253c0086b8SZbigniew Bodek {
2262306b72aSZbigniew Bodek 	bus_space_handle_t addr;
2273c0086b8SZbigniew Bodek 
2282306b72aSZbigniew Bodek 	addr = ((uint32_t)lmac << 20) + offset;
2292306b72aSZbigniew Bodek 
2302306b72aSZbigniew Bodek 	bus_write_8(bgx->reg_base, addr, val);
2313c0086b8SZbigniew Bodek }
2323c0086b8SZbigniew Bodek 
2332306b72aSZbigniew Bodek static void
bgx_reg_modify(struct bgx * bgx,uint8_t lmac,uint64_t offset,uint64_t val)2342306b72aSZbigniew Bodek bgx_reg_modify(struct bgx *bgx, uint8_t lmac, uint64_t offset, uint64_t val)
2353c0086b8SZbigniew Bodek {
2362306b72aSZbigniew Bodek 	bus_space_handle_t addr;
2373c0086b8SZbigniew Bodek 
2382306b72aSZbigniew Bodek 	addr = ((uint32_t)lmac << 20) + offset;
2392306b72aSZbigniew Bodek 
2402306b72aSZbigniew Bodek 	bus_write_8(bgx->reg_base, addr, val | bus_read_8(bgx->reg_base, addr));
2413c0086b8SZbigniew Bodek }
2423c0086b8SZbigniew Bodek 
2432306b72aSZbigniew Bodek static int
bgx_poll_reg(struct bgx * bgx,uint8_t lmac,uint64_t reg,uint64_t mask,boolean_t zero)2442306b72aSZbigniew Bodek bgx_poll_reg(struct bgx *bgx, uint8_t lmac, uint64_t reg, uint64_t mask,
2452306b72aSZbigniew Bodek     boolean_t zero)
2463c0086b8SZbigniew Bodek {
247d416a655SWojciech Macek 	int timeout = 10;
2482306b72aSZbigniew Bodek 	uint64_t reg_val;
2493c0086b8SZbigniew Bodek 
2503c0086b8SZbigniew Bodek 	while (timeout) {
2513c0086b8SZbigniew Bodek 		reg_val = bgx_reg_read(bgx, lmac, reg);
2523c0086b8SZbigniew Bodek 		if (zero && !(reg_val & mask))
2532306b72aSZbigniew Bodek 			return (0);
2543c0086b8SZbigniew Bodek 		if (!zero && (reg_val & mask))
2552306b72aSZbigniew Bodek 			return (0);
2562306b72aSZbigniew Bodek 
257d416a655SWojciech Macek 		DELAY(100);
2583c0086b8SZbigniew Bodek 		timeout--;
2593c0086b8SZbigniew Bodek 	}
2602306b72aSZbigniew Bodek 	return (ETIMEDOUT);
2613c0086b8SZbigniew Bodek }
2623c0086b8SZbigniew Bodek 
2633c0086b8SZbigniew Bodek /* Return number of BGX present in HW */
2642306b72aSZbigniew Bodek u_int
bgx_get_map(int node)2652306b72aSZbigniew Bodek bgx_get_map(int node)
2663c0086b8SZbigniew Bodek {
2673c0086b8SZbigniew Bodek 	int i;
2682306b72aSZbigniew Bodek 	u_int map = 0;
2693c0086b8SZbigniew Bodek 
2703c0086b8SZbigniew Bodek 	for (i = 0; i < MAX_BGX_PER_CN88XX; i++) {
2713c0086b8SZbigniew Bodek 		if (bgx_vnic[(node * MAX_BGX_PER_CN88XX) + i])
2723c0086b8SZbigniew Bodek 			map |= (1 << i);
2733c0086b8SZbigniew Bodek 	}
2743c0086b8SZbigniew Bodek 
2752306b72aSZbigniew Bodek 	return (map);
2763c0086b8SZbigniew Bodek }
2773c0086b8SZbigniew Bodek 
2783c0086b8SZbigniew Bodek /* Return number of LMAC configured for this BGX */
2792306b72aSZbigniew Bodek int
bgx_get_lmac_count(int node,int bgx_idx)2802306b72aSZbigniew Bodek bgx_get_lmac_count(int node, int bgx_idx)
2813c0086b8SZbigniew Bodek {
2823c0086b8SZbigniew Bodek 	struct bgx *bgx;
2833c0086b8SZbigniew Bodek 
2843c0086b8SZbigniew Bodek 	bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
2852306b72aSZbigniew Bodek 	if (bgx != NULL)
2862306b72aSZbigniew Bodek 		return (bgx->lmac_count);
2873c0086b8SZbigniew Bodek 
2882306b72aSZbigniew Bodek 	return (0);
2893c0086b8SZbigniew Bodek }
2903c0086b8SZbigniew Bodek 
2913c0086b8SZbigniew Bodek /* Returns the current link status of LMAC */
2922306b72aSZbigniew Bodek void
bgx_get_lmac_link_state(int node,int bgx_idx,int lmacid,void * status)2932306b72aSZbigniew Bodek bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status)
2943c0086b8SZbigniew Bodek {
2953c0086b8SZbigniew Bodek 	struct bgx_link_status *link = (struct bgx_link_status *)status;
2963c0086b8SZbigniew Bodek 	struct bgx *bgx;
2973c0086b8SZbigniew Bodek 	struct lmac *lmac;
2983c0086b8SZbigniew Bodek 
2993c0086b8SZbigniew Bodek 	bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
3002306b72aSZbigniew Bodek 	if (bgx == NULL)
3013c0086b8SZbigniew Bodek 		return;
3023c0086b8SZbigniew Bodek 
3033c0086b8SZbigniew Bodek 	lmac = &bgx->lmac[lmacid];
3043c0086b8SZbigniew Bodek 	link->link_up = lmac->link_up;
3053c0086b8SZbigniew Bodek 	link->duplex = lmac->last_duplex;
3063c0086b8SZbigniew Bodek 	link->speed = lmac->last_speed;
3073c0086b8SZbigniew Bodek }
3083c0086b8SZbigniew Bodek 
3092306b72aSZbigniew Bodek const uint8_t
bgx_get_lmac_mac(int node,int bgx_idx,int lmacid)3102306b72aSZbigniew Bodek *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid)
3113c0086b8SZbigniew Bodek {
3123c0086b8SZbigniew Bodek 	struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
3133c0086b8SZbigniew Bodek 
3142306b72aSZbigniew Bodek 	if (bgx != NULL)
3152306b72aSZbigniew Bodek 		return (bgx->lmac[lmacid].mac);
3163c0086b8SZbigniew Bodek 
3172306b72aSZbigniew Bodek 	return (NULL);
3183c0086b8SZbigniew Bodek }
3193c0086b8SZbigniew Bodek 
3202306b72aSZbigniew Bodek void
bgx_set_lmac_mac(int node,int bgx_idx,int lmacid,const uint8_t * mac)3212306b72aSZbigniew Bodek bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const uint8_t *mac)
3223c0086b8SZbigniew Bodek {
3233c0086b8SZbigniew Bodek 	struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
3243c0086b8SZbigniew Bodek 
3252306b72aSZbigniew Bodek 	if (bgx == NULL)
3263c0086b8SZbigniew Bodek 		return;
3273c0086b8SZbigniew Bodek 
3282306b72aSZbigniew Bodek 	memcpy(bgx->lmac[lmacid].mac, mac, ETHER_ADDR_LEN);
3293c0086b8SZbigniew Bodek }
3303c0086b8SZbigniew Bodek 
3312306b72aSZbigniew Bodek static void
bgx_sgmii_change_link_state(struct lmac * lmac)3322306b72aSZbigniew Bodek bgx_sgmii_change_link_state(struct lmac *lmac)
3333c0086b8SZbigniew Bodek {
3343c0086b8SZbigniew Bodek 	struct bgx *bgx = lmac->bgx;
3352306b72aSZbigniew Bodek 	uint64_t cmr_cfg;
3362306b72aSZbigniew Bodek 	uint64_t port_cfg = 0;
3372306b72aSZbigniew Bodek 	uint64_t misc_ctl = 0;
3383c0086b8SZbigniew Bodek 
3393c0086b8SZbigniew Bodek 	cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG);
3403c0086b8SZbigniew Bodek 	cmr_cfg &= ~CMR_EN;
3413c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
3423c0086b8SZbigniew Bodek 
3433c0086b8SZbigniew Bodek 	port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG);
3443c0086b8SZbigniew Bodek 	misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL);
3453c0086b8SZbigniew Bodek 
3463c0086b8SZbigniew Bodek 	if (lmac->link_up) {
3473c0086b8SZbigniew Bodek 		misc_ctl &= ~PCS_MISC_CTL_GMX_ENO;
3483c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_DUPLEX;
3493c0086b8SZbigniew Bodek 		port_cfg |=  (lmac->last_duplex << 2);
3503c0086b8SZbigniew Bodek 	} else {
3513c0086b8SZbigniew Bodek 		misc_ctl |= PCS_MISC_CTL_GMX_ENO;
3523c0086b8SZbigniew Bodek 	}
3533c0086b8SZbigniew Bodek 
3543c0086b8SZbigniew Bodek 	switch (lmac->last_speed) {
3553c0086b8SZbigniew Bodek 	case 10:
3563c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
3573c0086b8SZbigniew Bodek 		port_cfg |= GMI_PORT_CFG_SPEED_MSB;  /* speed_msb 1 */
3583c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
3593c0086b8SZbigniew Bodek 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
3603c0086b8SZbigniew Bodek 		misc_ctl |= 50; /* samp_pt */
3613c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
3623c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
3633c0086b8SZbigniew Bodek 		break;
3643c0086b8SZbigniew Bodek 	case 100:
3653c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
3663c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
3673c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
3683c0086b8SZbigniew Bodek 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
3693c0086b8SZbigniew Bodek 		misc_ctl |= 5; /* samp_pt */
3703c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
3713c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
3723c0086b8SZbigniew Bodek 		break;
3733c0086b8SZbigniew Bodek 	case 1000:
3743c0086b8SZbigniew Bodek 		port_cfg |= GMI_PORT_CFG_SPEED; /* speed 1 */
3753c0086b8SZbigniew Bodek 		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
3763c0086b8SZbigniew Bodek 		port_cfg |= GMI_PORT_CFG_SLOT_TIME; /* slottime 1 */
3773c0086b8SZbigniew Bodek 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
3783c0086b8SZbigniew Bodek 		misc_ctl |= 1; /* samp_pt */
3793c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 512);
3803c0086b8SZbigniew Bodek 		if (lmac->last_duplex)
3813c0086b8SZbigniew Bodek 			bgx_reg_write(bgx, lmac->lmacid,
3823c0086b8SZbigniew Bodek 				      BGX_GMP_GMI_TXX_BURST, 0);
3833c0086b8SZbigniew Bodek 		else
3843c0086b8SZbigniew Bodek 			bgx_reg_write(bgx, lmac->lmacid,
3853c0086b8SZbigniew Bodek 				      BGX_GMP_GMI_TXX_BURST, 8192);
3863c0086b8SZbigniew Bodek 		break;
3873c0086b8SZbigniew Bodek 	default:
3883c0086b8SZbigniew Bodek 		break;
3893c0086b8SZbigniew Bodek 	}
3903c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl);
3913c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg);
3923c0086b8SZbigniew Bodek 
3933c0086b8SZbigniew Bodek 	port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG);
3943c0086b8SZbigniew Bodek 
3953c0086b8SZbigniew Bodek 	/* renable lmac */
3963c0086b8SZbigniew Bodek 	cmr_cfg |= CMR_EN;
3973c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
3983c0086b8SZbigniew Bodek }
3993c0086b8SZbigniew Bodek 
4002306b72aSZbigniew Bodek static void
bgx_lmac_handler(void * arg)4012306b72aSZbigniew Bodek bgx_lmac_handler(void *arg)
4023c0086b8SZbigniew Bodek {
4032306b72aSZbigniew Bodek 	struct lmac *lmac;
4042306b72aSZbigniew Bodek 	int link, duplex, speed;
4053c0086b8SZbigniew Bodek 	int link_changed = 0;
4062306b72aSZbigniew Bodek 	int err;
4073c0086b8SZbigniew Bodek 
4082306b72aSZbigniew Bodek 	lmac = (struct lmac *)arg;
4093c0086b8SZbigniew Bodek 
4102306b72aSZbigniew Bodek 	err = LMAC_MEDIA_STATUS(lmac->phy_if_dev, lmac->lmacid,
4112306b72aSZbigniew Bodek 	    &link, &duplex, &speed);
4122306b72aSZbigniew Bodek 	if (err != 0)
4132306b72aSZbigniew Bodek 		goto out;
4142306b72aSZbigniew Bodek 
4152306b72aSZbigniew Bodek 	if (!link && lmac->last_link)
4163c0086b8SZbigniew Bodek 		link_changed = -1;
4173c0086b8SZbigniew Bodek 
4182306b72aSZbigniew Bodek 	if (link &&
4192306b72aSZbigniew Bodek 	    (lmac->last_duplex != duplex ||
4202306b72aSZbigniew Bodek 	     lmac->last_link != link ||
4212306b72aSZbigniew Bodek 	     lmac->last_speed != speed)) {
4223c0086b8SZbigniew Bodek 			link_changed = 1;
4233c0086b8SZbigniew Bodek 	}
4243c0086b8SZbigniew Bodek 
4252306b72aSZbigniew Bodek 	lmac->last_link = link;
4262306b72aSZbigniew Bodek 	lmac->last_speed = speed;
4272306b72aSZbigniew Bodek 	lmac->last_duplex = duplex;
4283c0086b8SZbigniew Bodek 
4293c0086b8SZbigniew Bodek 	if (!link_changed)
4302306b72aSZbigniew Bodek 		goto out;
4313c0086b8SZbigniew Bodek 
4323c0086b8SZbigniew Bodek 	if (link_changed > 0)
4333c0086b8SZbigniew Bodek 		lmac->link_up = true;
4343c0086b8SZbigniew Bodek 	else
4353c0086b8SZbigniew Bodek 		lmac->link_up = false;
4363c0086b8SZbigniew Bodek 
4373c0086b8SZbigniew Bodek 	if (lmac->is_sgmii)
4383c0086b8SZbigniew Bodek 		bgx_sgmii_change_link_state(lmac);
4393c0086b8SZbigniew Bodek 	else
4403c0086b8SZbigniew Bodek 		bgx_xaui_check_link(lmac);
4412306b72aSZbigniew Bodek 
4422306b72aSZbigniew Bodek out:
4432306b72aSZbigniew Bodek 	callout_reset(&lmac->check_link, hz * 2, bgx_lmac_handler, lmac);
4443c0086b8SZbigniew Bodek }
4453c0086b8SZbigniew Bodek 
4462306b72aSZbigniew Bodek uint64_t
bgx_get_rx_stats(int node,int bgx_idx,int lmac,int idx)4472306b72aSZbigniew Bodek bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx)
4483c0086b8SZbigniew Bodek {
4493c0086b8SZbigniew Bodek 	struct bgx *bgx;
4503c0086b8SZbigniew Bodek 
4513c0086b8SZbigniew Bodek 	bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
4522306b72aSZbigniew Bodek 	if (bgx == NULL)
4532306b72aSZbigniew Bodek 		return (0);
4543c0086b8SZbigniew Bodek 
4553c0086b8SZbigniew Bodek 	if (idx > 8)
4562306b72aSZbigniew Bodek 		lmac = (0);
4572306b72aSZbigniew Bodek 	return (bgx_reg_read(bgx, lmac, BGX_CMRX_RX_STAT0 + (idx * 8)));
4583c0086b8SZbigniew Bodek }
4593c0086b8SZbigniew Bodek 
4602306b72aSZbigniew Bodek uint64_t
bgx_get_tx_stats(int node,int bgx_idx,int lmac,int idx)4612306b72aSZbigniew Bodek bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx)
4623c0086b8SZbigniew Bodek {
4633c0086b8SZbigniew Bodek 	struct bgx *bgx;
4643c0086b8SZbigniew Bodek 
4653c0086b8SZbigniew Bodek 	bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
4662306b72aSZbigniew Bodek 	if (bgx == NULL)
4672306b72aSZbigniew Bodek 		return (0);
4683c0086b8SZbigniew Bodek 
4692306b72aSZbigniew Bodek 	return (bgx_reg_read(bgx, lmac, BGX_CMRX_TX_STAT0 + (idx * 8)));
4703c0086b8SZbigniew Bodek }
4713c0086b8SZbigniew Bodek 
4722306b72aSZbigniew Bodek static void
bgx_flush_dmac_addrs(struct bgx * bgx,int lmac)4732306b72aSZbigniew Bodek bgx_flush_dmac_addrs(struct bgx *bgx, int lmac)
4743c0086b8SZbigniew Bodek {
4752306b72aSZbigniew Bodek 	uint64_t offset;
4763c0086b8SZbigniew Bodek 
4773c0086b8SZbigniew Bodek 	while (bgx->lmac[lmac].dmac > 0) {
4782306b72aSZbigniew Bodek 		offset = ((bgx->lmac[lmac].dmac - 1) * sizeof(uint64_t)) +
4792306b72aSZbigniew Bodek 		    (lmac * MAX_DMAC_PER_LMAC * sizeof(uint64_t));
4803c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, 0);
4813c0086b8SZbigniew Bodek 		bgx->lmac[lmac].dmac--;
4823c0086b8SZbigniew Bodek 	}
4833c0086b8SZbigniew Bodek }
4843c0086b8SZbigniew Bodek 
4852306b72aSZbigniew Bodek void
bgx_add_dmac_addr(uint64_t dmac,int node,int bgx_idx,int lmac)4862306b72aSZbigniew Bodek bgx_add_dmac_addr(uint64_t dmac, int node, int bgx_idx, int lmac)
4873c0086b8SZbigniew Bodek {
4882306b72aSZbigniew Bodek 	uint64_t offset;
4893c0086b8SZbigniew Bodek 	struct bgx *bgx;
4903c0086b8SZbigniew Bodek 
4913c0086b8SZbigniew Bodek #ifdef BGX_IN_PROMISCUOUS_MODE
4923c0086b8SZbigniew Bodek 	return;
4933c0086b8SZbigniew Bodek #endif
4943c0086b8SZbigniew Bodek 
4953c0086b8SZbigniew Bodek 	bgx_idx += node * MAX_BGX_PER_CN88XX;
4963c0086b8SZbigniew Bodek 	bgx = bgx_vnic[bgx_idx];
4973c0086b8SZbigniew Bodek 
4983485f457SEd Maste 	if (bgx == NULL) {
4993485f457SEd Maste 		printf("BGX%d not yet initialized, ignoring DMAC addition\n",
5003c0086b8SZbigniew Bodek 		    bgx_idx);
5013c0086b8SZbigniew Bodek 		return;
5023c0086b8SZbigniew Bodek 	}
5033c0086b8SZbigniew Bodek 
5042306b72aSZbigniew Bodek 	dmac = dmac | (1UL << 48) | ((uint64_t)lmac << 49); /* Enable DMAC */
5053c0086b8SZbigniew Bodek 	if (bgx->lmac[lmac].dmac == MAX_DMAC_PER_LMAC) {
5062306b72aSZbigniew Bodek 		device_printf(bgx->dev,
5073c0086b8SZbigniew Bodek 		    "Max DMAC filters for LMAC%d reached, ignoring\n",
5083c0086b8SZbigniew Bodek 		    lmac);
5093c0086b8SZbigniew Bodek 		return;
5103c0086b8SZbigniew Bodek 	}
5113c0086b8SZbigniew Bodek 
5123c0086b8SZbigniew Bodek 	if (bgx->lmac[lmac].dmac == MAX_DMAC_PER_LMAC_TNS_BYPASS_MODE)
5133c0086b8SZbigniew Bodek 		bgx->lmac[lmac].dmac = 1;
5143c0086b8SZbigniew Bodek 
5152306b72aSZbigniew Bodek 	offset = (bgx->lmac[lmac].dmac * sizeof(uint64_t)) +
5162306b72aSZbigniew Bodek 	    (lmac * MAX_DMAC_PER_LMAC * sizeof(uint64_t));
5173c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + offset, dmac);
5183c0086b8SZbigniew Bodek 	bgx->lmac[lmac].dmac++;
5193c0086b8SZbigniew Bodek 
5203c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmac, BGX_CMRX_RX_DMAC_CTL,
5212306b72aSZbigniew Bodek 	    (CAM_ACCEPT << 3) | (MCAST_MODE_CAM_FILTER << 1) |
5222306b72aSZbigniew Bodek 	    (BCAST_ACCEPT << 0));
5233c0086b8SZbigniew Bodek }
5243c0086b8SZbigniew Bodek 
5253c0086b8SZbigniew Bodek /* Configure BGX LMAC in internal loopback mode */
5262306b72aSZbigniew Bodek void
bgx_lmac_internal_loopback(int node,int bgx_idx,int lmac_idx,boolean_t enable)5272306b72aSZbigniew Bodek bgx_lmac_internal_loopback(int node, int bgx_idx,
5282306b72aSZbigniew Bodek     int lmac_idx, boolean_t enable)
5293c0086b8SZbigniew Bodek {
5303c0086b8SZbigniew Bodek 	struct bgx *bgx;
5313c0086b8SZbigniew Bodek 	struct lmac *lmac;
5322306b72aSZbigniew Bodek 	uint64_t cfg;
5333c0086b8SZbigniew Bodek 
5343c0086b8SZbigniew Bodek 	bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
5352306b72aSZbigniew Bodek 	if (bgx == NULL)
5363c0086b8SZbigniew Bodek 		return;
5373c0086b8SZbigniew Bodek 
5383c0086b8SZbigniew Bodek 	lmac = &bgx->lmac[lmac_idx];
5393c0086b8SZbigniew Bodek 	if (lmac->is_sgmii) {
5403c0086b8SZbigniew Bodek 		cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL);
5413c0086b8SZbigniew Bodek 		if (enable)
5423c0086b8SZbigniew Bodek 			cfg |= PCS_MRX_CTL_LOOPBACK1;
5433c0086b8SZbigniew Bodek 		else
5443c0086b8SZbigniew Bodek 			cfg &= ~PCS_MRX_CTL_LOOPBACK1;
5453c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg);
5463c0086b8SZbigniew Bodek 	} else {
5473c0086b8SZbigniew Bodek 		cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1);
5483c0086b8SZbigniew Bodek 		if (enable)
5493c0086b8SZbigniew Bodek 			cfg |= SPU_CTL_LOOPBACK;
5503c0086b8SZbigniew Bodek 		else
5513c0086b8SZbigniew Bodek 			cfg &= ~SPU_CTL_LOOPBACK;
5523c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg);
5533c0086b8SZbigniew Bodek 	}
5543c0086b8SZbigniew Bodek }
5553c0086b8SZbigniew Bodek 
5562306b72aSZbigniew Bodek static int
bgx_lmac_sgmii_init(struct bgx * bgx,int lmacid)5572306b72aSZbigniew Bodek bgx_lmac_sgmii_init(struct bgx *bgx, int lmacid)
5583c0086b8SZbigniew Bodek {
5592306b72aSZbigniew Bodek 	uint64_t cfg;
5603c0086b8SZbigniew Bodek 
5613c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30);
5623c0086b8SZbigniew Bodek 	/* max packet size */
5633c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE);
5643c0086b8SZbigniew Bodek 
5653c0086b8SZbigniew Bodek 	/* Disable frame alignment if using preamble */
5663c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
5673c0086b8SZbigniew Bodek 	if (cfg & 1)
5683c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0);
5693c0086b8SZbigniew Bodek 
5703c0086b8SZbigniew Bodek 	/* Enable lmac */
5713c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
5723c0086b8SZbigniew Bodek 
5733c0086b8SZbigniew Bodek 	/* PCS reset */
5743c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET);
5753c0086b8SZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL,
5762306b72aSZbigniew Bodek 	    PCS_MRX_CTL_RESET, TRUE) != 0) {
5772306b72aSZbigniew Bodek 		device_printf(bgx->dev, "BGX PCS reset not completed\n");
5782306b72aSZbigniew Bodek 		return (ENXIO);
5793c0086b8SZbigniew Bodek 	}
5803c0086b8SZbigniew Bodek 
5813c0086b8SZbigniew Bodek 	/* power down, reset autoneg, autoneg enable */
5823c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
5833c0086b8SZbigniew Bodek 	cfg &= ~PCS_MRX_CTL_PWR_DN;
5843c0086b8SZbigniew Bodek 	cfg |= (PCS_MRX_CTL_RST_AN | PCS_MRX_CTL_AN_EN);
5853c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
5863c0086b8SZbigniew Bodek 
5873c0086b8SZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS,
5882306b72aSZbigniew Bodek 	    PCS_MRX_STATUS_AN_CPT, FALSE) != 0) {
5892306b72aSZbigniew Bodek 		device_printf(bgx->dev, "BGX AN_CPT not completed\n");
5902306b72aSZbigniew Bodek 		return (ENXIO);
5913c0086b8SZbigniew Bodek 	}
5923c0086b8SZbigniew Bodek 
5932306b72aSZbigniew Bodek 	return (0);
5943c0086b8SZbigniew Bodek }
5953c0086b8SZbigniew Bodek 
5962306b72aSZbigniew Bodek static int
bgx_lmac_xaui_init(struct bgx * bgx,int lmacid,int lmac_type)5972306b72aSZbigniew Bodek bgx_lmac_xaui_init(struct bgx *bgx, int lmacid, int lmac_type)
5983c0086b8SZbigniew Bodek {
5992306b72aSZbigniew Bodek 	uint64_t cfg;
6003c0086b8SZbigniew Bodek 
6013c0086b8SZbigniew Bodek 	/* Reset SPU */
6023c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET);
6032306b72aSZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1,
6042306b72aSZbigniew Bodek 	    SPU_CTL_RESET, TRUE) != 0) {
6052306b72aSZbigniew Bodek 		device_printf(bgx->dev, "BGX SPU reset not completed\n");
6062306b72aSZbigniew Bodek 		return (ENXIO);
6073c0086b8SZbigniew Bodek 	}
6083c0086b8SZbigniew Bodek 
6093c0086b8SZbigniew Bodek 	/* Disable LMAC */
6103c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
6113c0086b8SZbigniew Bodek 	cfg &= ~CMR_EN;
6123c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
6133c0086b8SZbigniew Bodek 
6143c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
6153c0086b8SZbigniew Bodek 	/* Set interleaved running disparity for RXAUI */
6162306b72aSZbigniew Bodek 	if (bgx->lmac_type != BGX_MODE_RXAUI) {
6173c0086b8SZbigniew Bodek 		bgx_reg_modify(bgx, lmacid,
6183c0086b8SZbigniew Bodek 		    BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS);
6192306b72aSZbigniew Bodek 	} else {
6203c0086b8SZbigniew Bodek 		bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL,
6213c0086b8SZbigniew Bodek 		    SPU_MISC_CTL_RX_DIS | SPU_MISC_CTL_INTLV_RDISP);
6222306b72aSZbigniew Bodek 	}
6233c0086b8SZbigniew Bodek 
6243c0086b8SZbigniew Bodek 	/* clear all interrupts */
6253c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT);
6263c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg);
6273c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT);
6283c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg);
6293c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
6303c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
6313c0086b8SZbigniew Bodek 
6323c0086b8SZbigniew Bodek 	if (bgx->use_training) {
6333c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00);
6343c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00);
6353c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00);
6363c0086b8SZbigniew Bodek 		/* training enable */
6372306b72aSZbigniew Bodek 		bgx_reg_modify(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL,
6382306b72aSZbigniew Bodek 		    SPU_PMD_CRTL_TRAIN_EN);
6393c0086b8SZbigniew Bodek 	}
6403c0086b8SZbigniew Bodek 
6413c0086b8SZbigniew Bodek 	/* Append FCS to each packet */
6423c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D);
6433c0086b8SZbigniew Bodek 
6443c0086b8SZbigniew Bodek 	/* Disable forward error correction */
6453c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL);
6463c0086b8SZbigniew Bodek 	cfg &= ~SPU_FEC_CTL_FEC_EN;
6473c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg);
6483c0086b8SZbigniew Bodek 
6493c0086b8SZbigniew Bodek 	/* Disable autoneg */
6503c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL);
6513c0086b8SZbigniew Bodek 	cfg = cfg & ~(SPU_AN_CTL_AN_EN | SPU_AN_CTL_XNP_EN);
6523c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg);
6533c0086b8SZbigniew Bodek 
6543c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV);
6553c0086b8SZbigniew Bodek 	if (bgx->lmac_type == BGX_MODE_10G_KR)
6563c0086b8SZbigniew Bodek 		cfg |= (1 << 23);
6573c0086b8SZbigniew Bodek 	else if (bgx->lmac_type == BGX_MODE_40G_KR)
6583c0086b8SZbigniew Bodek 		cfg |= (1 << 24);
6593c0086b8SZbigniew Bodek 	else
6603c0086b8SZbigniew Bodek 		cfg &= ~((1 << 23) | (1 << 24));
6612306b72aSZbigniew Bodek 	cfg = cfg & (~((1UL << 25) | (1UL << 22) | (1UL << 12)));
6623c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg);
6633c0086b8SZbigniew Bodek 
6643c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL);
6653c0086b8SZbigniew Bodek 	cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN;
6663c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg);
6673c0086b8SZbigniew Bodek 
6683c0086b8SZbigniew Bodek 	/* Enable lmac */
6693c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
6703c0086b8SZbigniew Bodek 
6713c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1);
6723c0086b8SZbigniew Bodek 	cfg &= ~SPU_CTL_LOW_POWER;
6733c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg);
6743c0086b8SZbigniew Bodek 
6753c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL);
6763c0086b8SZbigniew Bodek 	cfg &= ~SMU_TX_CTL_UNI_EN;
6773c0086b8SZbigniew Bodek 	cfg |= SMU_TX_CTL_DIC_EN;
6783c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
6793c0086b8SZbigniew Bodek 
6803c0086b8SZbigniew Bodek 	/* take lmac_count into account */
6813c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
6823c0086b8SZbigniew Bodek 	/* max packet size */
6833c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE);
6843c0086b8SZbigniew Bodek 
6852306b72aSZbigniew Bodek 	return (0);
6863c0086b8SZbigniew Bodek }
6873c0086b8SZbigniew Bodek 
6882306b72aSZbigniew Bodek static int
bgx_xaui_check_link(struct lmac * lmac)6892306b72aSZbigniew Bodek bgx_xaui_check_link(struct lmac *lmac)
6903c0086b8SZbigniew Bodek {
6913c0086b8SZbigniew Bodek 	struct bgx *bgx = lmac->bgx;
6923c0086b8SZbigniew Bodek 	int lmacid = lmac->lmacid;
6933c0086b8SZbigniew Bodek 	int lmac_type = bgx->lmac_type;
6942306b72aSZbigniew Bodek 	uint64_t cfg;
6953c0086b8SZbigniew Bodek 
6963c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, SPU_MISC_CTL_RX_DIS);
6973c0086b8SZbigniew Bodek 	if (bgx->use_training) {
6983c0086b8SZbigniew Bodek 		cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
6992306b72aSZbigniew Bodek 		if ((cfg & (1UL << 13)) == 0) {
7002306b72aSZbigniew Bodek 			cfg = (1UL << 13) | (1UL << 14);
7013c0086b8SZbigniew Bodek 			bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
7023c0086b8SZbigniew Bodek 			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL);
7032306b72aSZbigniew Bodek 			cfg |= (1UL << 0);
7043c0086b8SZbigniew Bodek 			bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg);
7052306b72aSZbigniew Bodek 			return (ENXIO);
7063c0086b8SZbigniew Bodek 		}
7073c0086b8SZbigniew Bodek 	}
7083c0086b8SZbigniew Bodek 
7093c0086b8SZbigniew Bodek 	/* wait for PCS to come out of reset */
7102306b72aSZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1,
7112306b72aSZbigniew Bodek 	    SPU_CTL_RESET, TRUE) != 0) {
7122306b72aSZbigniew Bodek 		device_printf(bgx->dev, "BGX SPU reset not completed\n");
7132306b72aSZbigniew Bodek 		return (ENXIO);
7143c0086b8SZbigniew Bodek 	}
7153c0086b8SZbigniew Bodek 
7163c0086b8SZbigniew Bodek 	if ((lmac_type == BGX_MODE_10G_KR) || (lmac_type == BGX_MODE_XFI) ||
7173c0086b8SZbigniew Bodek 	    (lmac_type == BGX_MODE_40G_KR) || (lmac_type == BGX_MODE_XLAUI)) {
7183c0086b8SZbigniew Bodek 		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1,
7192306b72aSZbigniew Bodek 		    SPU_BR_STATUS_BLK_LOCK, FALSE)) {
7202306b72aSZbigniew Bodek 			device_printf(bgx->dev,
7213c0086b8SZbigniew Bodek 			    "SPU_BR_STATUS_BLK_LOCK not completed\n");
7222306b72aSZbigniew Bodek 			return (ENXIO);
7233c0086b8SZbigniew Bodek 		}
7243c0086b8SZbigniew Bodek 	} else {
7253c0086b8SZbigniew Bodek 		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS,
7262306b72aSZbigniew Bodek 		    SPU_BX_STATUS_RX_ALIGN, FALSE) != 0) {
7272306b72aSZbigniew Bodek 			device_printf(bgx->dev,
7283c0086b8SZbigniew Bodek 			    "SPU_BX_STATUS_RX_ALIGN not completed\n");
7292306b72aSZbigniew Bodek 			return (ENXIO);
7303c0086b8SZbigniew Bodek 		}
7313c0086b8SZbigniew Bodek 	}
7323c0086b8SZbigniew Bodek 
7333c0086b8SZbigniew Bodek 	/* Clear rcvflt bit (latching high) and read it back */
7343c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
7353c0086b8SZbigniew Bodek 	if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
7362306b72aSZbigniew Bodek 		device_printf(bgx->dev, "Receive fault, retry training\n");
7373c0086b8SZbigniew Bodek 		if (bgx->use_training) {
7383c0086b8SZbigniew Bodek 			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
7392306b72aSZbigniew Bodek 			if ((cfg & (1UL << 13)) == 0) {
7402306b72aSZbigniew Bodek 				cfg = (1UL << 13) | (1UL << 14);
7413c0086b8SZbigniew Bodek 				bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
7423c0086b8SZbigniew Bodek 				cfg = bgx_reg_read(bgx, lmacid,
7433c0086b8SZbigniew Bodek 				    BGX_SPUX_BR_PMD_CRTL);
7442306b72aSZbigniew Bodek 				cfg |= (1UL << 0);
7453c0086b8SZbigniew Bodek 				bgx_reg_write(bgx, lmacid,
7463c0086b8SZbigniew Bodek 				    BGX_SPUX_BR_PMD_CRTL, cfg);
7472306b72aSZbigniew Bodek 				return (ENXIO);
7483c0086b8SZbigniew Bodek 			}
7493c0086b8SZbigniew Bodek 		}
7502306b72aSZbigniew Bodek 		return (ENXIO);
7513c0086b8SZbigniew Bodek 	}
7523c0086b8SZbigniew Bodek 
7533c0086b8SZbigniew Bodek 	/* Wait for MAC RX to be ready */
7543c0086b8SZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_RX_CTL,
7552306b72aSZbigniew Bodek 	    SMU_RX_CTL_STATUS, TRUE) != 0) {
7562306b72aSZbigniew Bodek 		device_printf(bgx->dev, "SMU RX link not okay\n");
7572306b72aSZbigniew Bodek 		return (ENXIO);
7583c0086b8SZbigniew Bodek 	}
7593c0086b8SZbigniew Bodek 
7603c0086b8SZbigniew Bodek 	/* Wait for BGX RX to be idle */
7612306b72aSZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL,
7622306b72aSZbigniew Bodek 	    SMU_CTL_RX_IDLE, FALSE) != 0) {
7632306b72aSZbigniew Bodek 		device_printf(bgx->dev, "SMU RX not idle\n");
7642306b72aSZbigniew Bodek 		return (ENXIO);
7653c0086b8SZbigniew Bodek 	}
7663c0086b8SZbigniew Bodek 
7673c0086b8SZbigniew Bodek 	/* Wait for BGX TX to be idle */
7682306b72aSZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL,
7692306b72aSZbigniew Bodek 	    SMU_CTL_TX_IDLE, FALSE) != 0) {
7702306b72aSZbigniew Bodek 		device_printf(bgx->dev, "SMU TX not idle\n");
7712306b72aSZbigniew Bodek 		return (ENXIO);
7723c0086b8SZbigniew Bodek 	}
7733c0086b8SZbigniew Bodek 
7742306b72aSZbigniew Bodek 	if ((bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) &
7752306b72aSZbigniew Bodek 	    SPU_STATUS2_RCVFLT) != 0) {
7762306b72aSZbigniew Bodek 		device_printf(bgx->dev, "Receive fault\n");
7772306b72aSZbigniew Bodek 		return (ENXIO);
7783c0086b8SZbigniew Bodek 	}
7793c0086b8SZbigniew Bodek 
7803c0086b8SZbigniew Bodek 	/* Receive link is latching low. Force it high and verify it */
7813c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK);
7823c0086b8SZbigniew Bodek 	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_STATUS1,
7832306b72aSZbigniew Bodek 	    SPU_STATUS1_RCV_LNK, FALSE) != 0) {
7842306b72aSZbigniew Bodek 		device_printf(bgx->dev, "SPU receive link down\n");
7852306b72aSZbigniew Bodek 		return (ENXIO);
7863c0086b8SZbigniew Bodek 	}
7873c0086b8SZbigniew Bodek 
7883c0086b8SZbigniew Bodek 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL);
7893c0086b8SZbigniew Bodek 	cfg &= ~SPU_MISC_CTL_RX_DIS;
7903c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg);
7912306b72aSZbigniew Bodek 	return (0);
7923c0086b8SZbigniew Bodek }
7933c0086b8SZbigniew Bodek 
7942306b72aSZbigniew Bodek static void
bgx_poll_for_link(void * arg)7952306b72aSZbigniew Bodek bgx_poll_for_link(void *arg)
7963c0086b8SZbigniew Bodek {
7973c0086b8SZbigniew Bodek 	struct lmac *lmac;
7982306b72aSZbigniew Bodek 	uint64_t link;
7993c0086b8SZbigniew Bodek 
8002306b72aSZbigniew Bodek 	lmac = (struct lmac *)arg;
8013c0086b8SZbigniew Bodek 
8023c0086b8SZbigniew Bodek 	/* Receive link is latching low. Force it high and verify it */
8033c0086b8SZbigniew Bodek 	bgx_reg_modify(lmac->bgx, lmac->lmacid,
8043c0086b8SZbigniew Bodek 		       BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK);
8053c0086b8SZbigniew Bodek 	bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1,
8063c0086b8SZbigniew Bodek 		     SPU_STATUS1_RCV_LNK, false);
8073c0086b8SZbigniew Bodek 
8083c0086b8SZbigniew Bodek 	link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1);
8093c0086b8SZbigniew Bodek 	if (link & SPU_STATUS1_RCV_LNK) {
8103c0086b8SZbigniew Bodek 		lmac->link_up = 1;
8113c0086b8SZbigniew Bodek 		if (lmac->bgx->lmac_type == BGX_MODE_XLAUI)
8123c0086b8SZbigniew Bodek 			lmac->last_speed = 40000;
8133c0086b8SZbigniew Bodek 		else
8143c0086b8SZbigniew Bodek 			lmac->last_speed = 10000;
8153c0086b8SZbigniew Bodek 		lmac->last_duplex = 1;
8163c0086b8SZbigniew Bodek 	} else {
8173c0086b8SZbigniew Bodek 		lmac->link_up = 0;
8183c0086b8SZbigniew Bodek 	}
8193c0086b8SZbigniew Bodek 
8203c0086b8SZbigniew Bodek 	if (lmac->last_link != lmac->link_up) {
8213c0086b8SZbigniew Bodek 		lmac->last_link = lmac->link_up;
8223c0086b8SZbigniew Bodek 		if (lmac->link_up)
8233c0086b8SZbigniew Bodek 			bgx_xaui_check_link(lmac);
8243c0086b8SZbigniew Bodek 	}
8253c0086b8SZbigniew Bodek 
8262306b72aSZbigniew Bodek 	callout_reset(&lmac->check_link, hz * 2, bgx_poll_for_link, lmac);
8273c0086b8SZbigniew Bodek }
8283c0086b8SZbigniew Bodek 
8292306b72aSZbigniew Bodek static int
bgx_lmac_enable(struct bgx * bgx,uint8_t lmacid)8302306b72aSZbigniew Bodek bgx_lmac_enable(struct bgx *bgx, uint8_t lmacid)
8313c0086b8SZbigniew Bodek {
8322306b72aSZbigniew Bodek 	uint64_t __unused dmac_bcast = (1UL << 48) - 1;
8333c0086b8SZbigniew Bodek 	struct lmac *lmac;
8342306b72aSZbigniew Bodek 	uint64_t cfg;
8353c0086b8SZbigniew Bodek 
8363c0086b8SZbigniew Bodek 	lmac = &bgx->lmac[lmacid];
8373c0086b8SZbigniew Bodek 	lmac->bgx = bgx;
8383c0086b8SZbigniew Bodek 
8393c0086b8SZbigniew Bodek 	if (bgx->lmac_type == BGX_MODE_SGMII) {
8403c0086b8SZbigniew Bodek 		lmac->is_sgmii = 1;
8412306b72aSZbigniew Bodek 		if (bgx_lmac_sgmii_init(bgx, lmacid) != 0)
8423c0086b8SZbigniew Bodek 			return -1;
8433c0086b8SZbigniew Bodek 	} else {
8443c0086b8SZbigniew Bodek 		lmac->is_sgmii = 0;
8453c0086b8SZbigniew Bodek 		if (bgx_lmac_xaui_init(bgx, lmacid, bgx->lmac_type))
8463c0086b8SZbigniew Bodek 			return -1;
8473c0086b8SZbigniew Bodek 	}
8483c0086b8SZbigniew Bodek 
8493c0086b8SZbigniew Bodek 	if (lmac->is_sgmii) {
8503c0086b8SZbigniew Bodek 		cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
8512306b72aSZbigniew Bodek 		cfg |= ((1UL << 2) | (1UL << 1)); /* FCS and PAD */
8523c0086b8SZbigniew Bodek 		bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg);
8533c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1);
8543c0086b8SZbigniew Bodek 	} else {
8553c0086b8SZbigniew Bodek 		cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND);
8562306b72aSZbigniew Bodek 		cfg |= ((1UL << 2) | (1UL << 1)); /* FCS and PAD */
8573c0086b8SZbigniew Bodek 		bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg);
8583c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
8593c0086b8SZbigniew Bodek 	}
8603c0086b8SZbigniew Bodek 
8613c0086b8SZbigniew Bodek 	/* Enable lmac */
8623c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG,
8633c0086b8SZbigniew Bodek 		       CMR_EN | CMR_PKT_RX_EN | CMR_PKT_TX_EN);
8643c0086b8SZbigniew Bodek 
8653c0086b8SZbigniew Bodek 	/* Restore default cfg, incase low level firmware changed it */
8663c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03);
8673c0086b8SZbigniew Bodek 
8683c0086b8SZbigniew Bodek 	/* Add broadcast MAC into all LMAC's DMAC filters */
8693c0086b8SZbigniew Bodek 	bgx_add_dmac_addr(dmac_bcast, 0, bgx->bgx_id, lmacid);
8703c0086b8SZbigniew Bodek 
8713c0086b8SZbigniew Bodek 	if ((bgx->lmac_type != BGX_MODE_XFI) &&
8722306b72aSZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_XAUI) &&
8733c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_XLAUI) &&
8743c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_40G_KR) &&
8753c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_10G_KR)) {
8762306b72aSZbigniew Bodek 		if (lmac->phy_if_dev == NULL) {
8772306b72aSZbigniew Bodek 			device_printf(bgx->dev,
8782306b72aSZbigniew Bodek 			    "LMAC%d missing interface to PHY\n", lmacid);
8792306b72aSZbigniew Bodek 			return (ENXIO);
8802306b72aSZbigniew Bodek 		}
8813c0086b8SZbigniew Bodek 
8822306b72aSZbigniew Bodek 		if (LMAC_PHY_CONNECT(lmac->phy_if_dev, lmac->phyaddr,
8832306b72aSZbigniew Bodek 		    lmacid) != 0) {
8842306b72aSZbigniew Bodek 			device_printf(bgx->dev,
8852306b72aSZbigniew Bodek 			    "LMAC%d could not connect to PHY\n", lmacid);
8862306b72aSZbigniew Bodek 			return (ENXIO);
8872306b72aSZbigniew Bodek 		}
8882306b72aSZbigniew Bodek 		mtx_init(&lmac->check_link_mtx, "BGX link poll", NULL, MTX_DEF);
8892306b72aSZbigniew Bodek 		callout_init_mtx(&lmac->check_link, &lmac->check_link_mtx, 0);
8902306b72aSZbigniew Bodek 		mtx_lock(&lmac->check_link_mtx);
8912306b72aSZbigniew Bodek 		bgx_lmac_handler(lmac);
8922306b72aSZbigniew Bodek 		mtx_unlock(&lmac->check_link_mtx);
8933c0086b8SZbigniew Bodek 	} else {
8942306b72aSZbigniew Bodek 		mtx_init(&lmac->check_link_mtx, "BGX link poll", NULL, MTX_DEF);
8952306b72aSZbigniew Bodek 		callout_init_mtx(&lmac->check_link, &lmac->check_link_mtx, 0);
8962306b72aSZbigniew Bodek 		mtx_lock(&lmac->check_link_mtx);
8972306b72aSZbigniew Bodek 		bgx_poll_for_link(lmac);
8982306b72aSZbigniew Bodek 		mtx_unlock(&lmac->check_link_mtx);
8993c0086b8SZbigniew Bodek 	}
9003c0086b8SZbigniew Bodek 
9012306b72aSZbigniew Bodek 	return (0);
9023c0086b8SZbigniew Bodek }
9033c0086b8SZbigniew Bodek 
9042306b72aSZbigniew Bodek static void
bgx_lmac_disable(struct bgx * bgx,uint8_t lmacid)9052306b72aSZbigniew Bodek bgx_lmac_disable(struct bgx *bgx, uint8_t lmacid)
9063c0086b8SZbigniew Bodek {
9073c0086b8SZbigniew Bodek 	struct lmac *lmac;
9082306b72aSZbigniew Bodek 	uint64_t cmrx_cfg;
9093c0086b8SZbigniew Bodek 
9103c0086b8SZbigniew Bodek 	lmac = &bgx->lmac[lmacid];
9112306b72aSZbigniew Bodek 
9122306b72aSZbigniew Bodek 	/* Stop callout */
9132306b72aSZbigniew Bodek 	callout_drain(&lmac->check_link);
9142306b72aSZbigniew Bodek 	mtx_destroy(&lmac->check_link_mtx);
9153c0086b8SZbigniew Bodek 
9163c0086b8SZbigniew Bodek 	cmrx_cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
9173c0086b8SZbigniew Bodek 	cmrx_cfg &= ~(1 << 15);
9183c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cmrx_cfg);
9193c0086b8SZbigniew Bodek 	bgx_flush_dmac_addrs(bgx, lmacid);
9203c0086b8SZbigniew Bodek 
9213c0086b8SZbigniew Bodek 	if ((bgx->lmac_type != BGX_MODE_XFI) &&
9223c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_XLAUI) &&
9233c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_40G_KR) &&
9243c0086b8SZbigniew Bodek 	    (bgx->lmac_type != BGX_MODE_10G_KR)) {
9252306b72aSZbigniew Bodek 		if (lmac->phy_if_dev == NULL) {
9262306b72aSZbigniew Bodek 			device_printf(bgx->dev,
9272306b72aSZbigniew Bodek 			    "LMAC%d missing interface to PHY\n", lmacid);
9282306b72aSZbigniew Bodek 			return;
9293c0086b8SZbigniew Bodek 		}
9302306b72aSZbigniew Bodek 		if (LMAC_PHY_DISCONNECT(lmac->phy_if_dev, lmac->phyaddr,
9312306b72aSZbigniew Bodek 		    lmacid) != 0) {
9322306b72aSZbigniew Bodek 			device_printf(bgx->dev,
9332306b72aSZbigniew Bodek 			    "LMAC%d could not disconnect PHY\n", lmacid);
9342306b72aSZbigniew Bodek 			return;
9352306b72aSZbigniew Bodek 		}
9362306b72aSZbigniew Bodek 		lmac->phy_if_dev = NULL;
9372306b72aSZbigniew Bodek 	}
9383c0086b8SZbigniew Bodek }
9393c0086b8SZbigniew Bodek 
9402306b72aSZbigniew Bodek static void
bgx_set_num_ports(struct bgx * bgx)9412306b72aSZbigniew Bodek bgx_set_num_ports(struct bgx *bgx)
9423c0086b8SZbigniew Bodek {
9432306b72aSZbigniew Bodek 	uint64_t lmac_count;
9443c0086b8SZbigniew Bodek 
9453c0086b8SZbigniew Bodek 	switch (bgx->qlm_mode) {
9463c0086b8SZbigniew Bodek 	case QLM_MODE_SGMII:
9473c0086b8SZbigniew Bodek 		bgx->lmac_count = 4;
9483c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_SGMII;
9493c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0;
9503c0086b8SZbigniew Bodek 		break;
9513c0086b8SZbigniew Bodek 	case QLM_MODE_XAUI_1X4:
9523c0086b8SZbigniew Bodek 		bgx->lmac_count = 1;
9533c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_XAUI;
9543c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0xE4;
9553c0086b8SZbigniew Bodek 			break;
9563c0086b8SZbigniew Bodek 	case QLM_MODE_RXAUI_2X2:
9573c0086b8SZbigniew Bodek 		bgx->lmac_count = 2;
9583c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_RXAUI;
9593c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0xE4;
9603c0086b8SZbigniew Bodek 			break;
9613c0086b8SZbigniew Bodek 	case QLM_MODE_XFI_4X1:
9623c0086b8SZbigniew Bodek 		bgx->lmac_count = 4;
9633c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_XFI;
9643c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0;
9653c0086b8SZbigniew Bodek 		break;
9663c0086b8SZbigniew Bodek 	case QLM_MODE_XLAUI_1X4:
9673c0086b8SZbigniew Bodek 		bgx->lmac_count = 1;
9683c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_XLAUI;
9693c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0xE4;
9703c0086b8SZbigniew Bodek 		break;
9713c0086b8SZbigniew Bodek 	case QLM_MODE_10G_KR_4X1:
9723c0086b8SZbigniew Bodek 		bgx->lmac_count = 4;
9733c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_10G_KR;
9743c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0;
9753c0086b8SZbigniew Bodek 		bgx->use_training = 1;
9763c0086b8SZbigniew Bodek 		break;
9773c0086b8SZbigniew Bodek 	case QLM_MODE_40G_KR4_1X4:
9783c0086b8SZbigniew Bodek 		bgx->lmac_count = 1;
9793c0086b8SZbigniew Bodek 		bgx->lmac_type = BGX_MODE_40G_KR;
9803c0086b8SZbigniew Bodek 		bgx->lane_to_sds = 0xE4;
9813c0086b8SZbigniew Bodek 		bgx->use_training = 1;
9823c0086b8SZbigniew Bodek 		break;
9833c0086b8SZbigniew Bodek 	default:
9843c0086b8SZbigniew Bodek 		bgx->lmac_count = 0;
9853c0086b8SZbigniew Bodek 		break;
9863c0086b8SZbigniew Bodek 	}
9873c0086b8SZbigniew Bodek 
9882306b72aSZbigniew Bodek 	/*
9892306b72aSZbigniew Bodek 	 * Check if low level firmware has programmed LMAC count
9903c0086b8SZbigniew Bodek 	 * based on board type, if yes consider that otherwise
9913c0086b8SZbigniew Bodek 	 * the default static values
9923c0086b8SZbigniew Bodek 	 */
9933c0086b8SZbigniew Bodek 	lmac_count = bgx_reg_read(bgx, 0, BGX_CMR_RX_LMACS) & 0x7;
9943c0086b8SZbigniew Bodek 	if (lmac_count != 4)
9953c0086b8SZbigniew Bodek 		bgx->lmac_count = lmac_count;
9963c0086b8SZbigniew Bodek }
9973c0086b8SZbigniew Bodek 
9982306b72aSZbigniew Bodek static void
bgx_init_hw(struct bgx * bgx)9992306b72aSZbigniew Bodek bgx_init_hw(struct bgx *bgx)
10003c0086b8SZbigniew Bodek {
10013c0086b8SZbigniew Bodek 	int i;
10023c0086b8SZbigniew Bodek 
10033c0086b8SZbigniew Bodek 	bgx_set_num_ports(bgx);
10043c0086b8SZbigniew Bodek 
10053c0086b8SZbigniew Bodek 	bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP);
10063c0086b8SZbigniew Bodek 	if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS))
10072306b72aSZbigniew Bodek 		device_printf(bgx->dev, "BGX%d BIST failed\n", bgx->bgx_id);
10083c0086b8SZbigniew Bodek 
10093c0086b8SZbigniew Bodek 	/* Set lmac type and lane2serdes mapping */
10103c0086b8SZbigniew Bodek 	for (i = 0; i < bgx->lmac_count; i++) {
10113c0086b8SZbigniew Bodek 		if (bgx->lmac_type == BGX_MODE_RXAUI) {
10123c0086b8SZbigniew Bodek 			if (i)
10133c0086b8SZbigniew Bodek 				bgx->lane_to_sds = 0x0e;
10143c0086b8SZbigniew Bodek 			else
10153c0086b8SZbigniew Bodek 				bgx->lane_to_sds = 0x04;
10163c0086b8SZbigniew Bodek 			bgx_reg_write(bgx, i, BGX_CMRX_CFG,
10173c0086b8SZbigniew Bodek 			    (bgx->lmac_type << 8) | bgx->lane_to_sds);
10183c0086b8SZbigniew Bodek 			continue;
10193c0086b8SZbigniew Bodek 		}
10203c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, i, BGX_CMRX_CFG,
10213c0086b8SZbigniew Bodek 		    (bgx->lmac_type << 8) | (bgx->lane_to_sds + i));
10223c0086b8SZbigniew Bodek 		bgx->lmac[i].lmacid_bd = lmac_count;
10233c0086b8SZbigniew Bodek 		lmac_count++;
10243c0086b8SZbigniew Bodek 	}
10253c0086b8SZbigniew Bodek 
10263c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, bgx->lmac_count);
10273c0086b8SZbigniew Bodek 	bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, bgx->lmac_count);
10283c0086b8SZbigniew Bodek 
10293c0086b8SZbigniew Bodek 	/* Set the backpressure AND mask */
10302306b72aSZbigniew Bodek 	for (i = 0; i < bgx->lmac_count; i++) {
10313c0086b8SZbigniew Bodek 		bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND,
10322306b72aSZbigniew Bodek 		    ((1UL << MAX_BGX_CHANS_PER_LMAC) - 1) <<
10333c0086b8SZbigniew Bodek 		    (i * MAX_BGX_CHANS_PER_LMAC));
10342306b72aSZbigniew Bodek 	}
10353c0086b8SZbigniew Bodek 
10363c0086b8SZbigniew Bodek 	/* Disable all MAC filtering */
10373c0086b8SZbigniew Bodek 	for (i = 0; i < RX_DMAC_COUNT; i++)
10383c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00);
10393c0086b8SZbigniew Bodek 
10403c0086b8SZbigniew Bodek 	/* Disable MAC steering (NCSI traffic) */
10413c0086b8SZbigniew Bodek 	for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++)
10423c0086b8SZbigniew Bodek 		bgx_reg_write(bgx, 0, BGX_CMR_RX_STREERING + (i * 8), 0x00);
10433c0086b8SZbigniew Bodek }
10443c0086b8SZbigniew Bodek 
10452306b72aSZbigniew Bodek static void
bgx_get_qlm_mode(struct bgx * bgx)10462306b72aSZbigniew Bodek bgx_get_qlm_mode(struct bgx *bgx)
10473c0086b8SZbigniew Bodek {
1048aeb665b5SEd Maste 	device_t dev = bgx->dev;
10493c0086b8SZbigniew Bodek 	int lmac_type;
10503c0086b8SZbigniew Bodek 	int train_en;
10513c0086b8SZbigniew Bodek 
10523c0086b8SZbigniew Bodek 	/* Read LMAC0 type to figure out QLM mode
10533c0086b8SZbigniew Bodek 	 * This is configured by low level firmware
10543c0086b8SZbigniew Bodek 	 */
10553c0086b8SZbigniew Bodek 	lmac_type = bgx_reg_read(bgx, 0, BGX_CMRX_CFG);
10563c0086b8SZbigniew Bodek 	lmac_type = (lmac_type >> 8) & 0x07;
10573c0086b8SZbigniew Bodek 
10583c0086b8SZbigniew Bodek 	train_en = bgx_reg_read(bgx, 0, BGX_SPUX_BR_PMD_CRTL) &
10593c0086b8SZbigniew Bodek 	    SPU_PMD_CRTL_TRAIN_EN;
10603c0086b8SZbigniew Bodek 
10613c0086b8SZbigniew Bodek 	switch (lmac_type) {
10623c0086b8SZbigniew Bodek 	case BGX_MODE_SGMII:
10633c0086b8SZbigniew Bodek 		bgx->qlm_mode = QLM_MODE_SGMII;
10642306b72aSZbigniew Bodek 		if (bootverbose) {
10652306b72aSZbigniew Bodek 			device_printf(dev, "BGX%d QLM mode: SGMII\n",
10662306b72aSZbigniew Bodek 			    bgx->bgx_id);
10672306b72aSZbigniew Bodek 		}
10683c0086b8SZbigniew Bodek 		break;
10693c0086b8SZbigniew Bodek 	case BGX_MODE_XAUI:
10703c0086b8SZbigniew Bodek 		bgx->qlm_mode = QLM_MODE_XAUI_1X4;
10712306b72aSZbigniew Bodek 		if (bootverbose) {
10722306b72aSZbigniew Bodek 			device_printf(dev, "BGX%d QLM mode: XAUI\n",
10732306b72aSZbigniew Bodek 			    bgx->bgx_id);
10742306b72aSZbigniew Bodek 		}
10753c0086b8SZbigniew Bodek 		break;
10763c0086b8SZbigniew Bodek 	case BGX_MODE_RXAUI:
10773c0086b8SZbigniew Bodek 		bgx->qlm_mode = QLM_MODE_RXAUI_2X2;
10782306b72aSZbigniew Bodek 		if (bootverbose) {
10792306b72aSZbigniew Bodek 			device_printf(dev, "BGX%d QLM mode: RXAUI\n",
10802306b72aSZbigniew Bodek 			    bgx->bgx_id);
10812306b72aSZbigniew Bodek 		}
10823c0086b8SZbigniew Bodek 		break;
10833c0086b8SZbigniew Bodek 	case BGX_MODE_XFI:
10843c0086b8SZbigniew Bodek 		if (!train_en) {
10853c0086b8SZbigniew Bodek 			bgx->qlm_mode = QLM_MODE_XFI_4X1;
10862306b72aSZbigniew Bodek 			if (bootverbose) {
10872306b72aSZbigniew Bodek 				device_printf(dev, "BGX%d QLM mode: XFI\n",
10882306b72aSZbigniew Bodek 				    bgx->bgx_id);
10892306b72aSZbigniew Bodek 			}
10903c0086b8SZbigniew Bodek 		} else {
10913c0086b8SZbigniew Bodek 			bgx->qlm_mode = QLM_MODE_10G_KR_4X1;
10922306b72aSZbigniew Bodek 			if (bootverbose) {
10932306b72aSZbigniew Bodek 				device_printf(dev, "BGX%d QLM mode: 10G_KR\n",
10942306b72aSZbigniew Bodek 				    bgx->bgx_id);
10952306b72aSZbigniew Bodek 			}
10963c0086b8SZbigniew Bodek 		}
10973c0086b8SZbigniew Bodek 		break;
10983c0086b8SZbigniew Bodek 	case BGX_MODE_XLAUI:
10993c0086b8SZbigniew Bodek 		if (!train_en) {
11003c0086b8SZbigniew Bodek 			bgx->qlm_mode = QLM_MODE_XLAUI_1X4;
11012306b72aSZbigniew Bodek 			if (bootverbose) {
11022306b72aSZbigniew Bodek 				device_printf(dev, "BGX%d QLM mode: XLAUI\n",
11032306b72aSZbigniew Bodek 				    bgx->bgx_id);
11042306b72aSZbigniew Bodek 			}
11053c0086b8SZbigniew Bodek 		} else {
11063c0086b8SZbigniew Bodek 			bgx->qlm_mode = QLM_MODE_40G_KR4_1X4;
11072306b72aSZbigniew Bodek 			if (bootverbose) {
11082306b72aSZbigniew Bodek 				device_printf(dev, "BGX%d QLM mode: 40G_KR4\n",
11092306b72aSZbigniew Bodek 				    bgx->bgx_id);
11102306b72aSZbigniew Bodek 			}
11113c0086b8SZbigniew Bodek 		}
11123c0086b8SZbigniew Bodek 		break;
11133c0086b8SZbigniew Bodek 	default:
11143c0086b8SZbigniew Bodek 		bgx->qlm_mode = QLM_MODE_SGMII;
11152306b72aSZbigniew Bodek 		if (bootverbose) {
11162306b72aSZbigniew Bodek 			device_printf(dev, "BGX%d QLM default mode: SGMII\n",
11172306b72aSZbigniew Bodek 			    bgx->bgx_id);
11182306b72aSZbigniew Bodek 		}
11193c0086b8SZbigniew Bodek 	}
11203c0086b8SZbigniew Bodek }
11213c0086b8SZbigniew Bodek 
11222306b72aSZbigniew Bodek static int
bgx_init_phy(struct bgx * bgx)11232306b72aSZbigniew Bodek bgx_init_phy(struct bgx *bgx)
11243c0086b8SZbigniew Bodek {
11253c0086b8SZbigniew Bodek 	int err;
11263c0086b8SZbigniew Bodek 
11272306b72aSZbigniew Bodek 	/* By default we fail */
11282306b72aSZbigniew Bodek 	err = ENXIO;
11292306b72aSZbigniew Bodek #ifdef FDT
11302306b72aSZbigniew Bodek 	err = bgx_fdt_init_phy(bgx);
11312306b72aSZbigniew Bodek #endif
11322306b72aSZbigniew Bodek #ifdef ACPI
11332306b72aSZbigniew Bodek 	if (err != 0) {
11342306b72aSZbigniew Bodek 		/* ARM64TODO: Add ACPI function here */
11353c0086b8SZbigniew Bodek 	}
11362306b72aSZbigniew Bodek #endif
11372306b72aSZbigniew Bodek 	return (err);
11383c0086b8SZbigniew Bodek }
1139