14863dea3SSunil Goutham /*
24863dea3SSunil Goutham  * Copyright (C) 2015 Cavium, Inc.
34863dea3SSunil Goutham  *
44863dea3SSunil Goutham  * This program is free software; you can redistribute it and/or modify it
54863dea3SSunil Goutham  * under the terms of version 2 of the GNU General Public License
64863dea3SSunil Goutham  * as published by the Free Software Foundation.
74863dea3SSunil Goutham  */
84863dea3SSunil Goutham 
946b903a0SDavid Daney #include <linux/acpi.h>
104863dea3SSunil Goutham #include <linux/module.h>
114863dea3SSunil Goutham #include <linux/interrupt.h>
124863dea3SSunil Goutham #include <linux/pci.h>
134863dea3SSunil Goutham #include <linux/netdevice.h>
144863dea3SSunil Goutham #include <linux/etherdevice.h>
154863dea3SSunil Goutham #include <linux/phy.h>
164863dea3SSunil Goutham #include <linux/of.h>
174863dea3SSunil Goutham #include <linux/of_mdio.h>
184863dea3SSunil Goutham #include <linux/of_net.h>
194863dea3SSunil Goutham 
204863dea3SSunil Goutham #include "nic_reg.h"
214863dea3SSunil Goutham #include "nic.h"
224863dea3SSunil Goutham #include "thunder_bgx.h"
234863dea3SSunil Goutham 
246b9e6547SVadim Lomovtsev #define DRV_NAME	"thunder_bgx"
254863dea3SSunil Goutham #define DRV_VERSION	"1.0"
264863dea3SSunil Goutham 
27f8ad1f3fSVadim Lomovtsev /* RX_DMAC_CTL configuration */
28f8ad1f3fSVadim Lomovtsev enum MCAST_MODE {
29f8ad1f3fSVadim Lomovtsev 		MCAST_MODE_REJECT = 0x0,
30f8ad1f3fSVadim Lomovtsev 		MCAST_MODE_ACCEPT = 0x1,
31f8ad1f3fSVadim Lomovtsev 		MCAST_MODE_CAM_FILTER = 0x2,
32f8ad1f3fSVadim Lomovtsev 		RSVD = 0x3
33f8ad1f3fSVadim Lomovtsev };
34f8ad1f3fSVadim Lomovtsev 
35f8ad1f3fSVadim Lomovtsev #define BCAST_ACCEPT      BIT(0)
36f8ad1f3fSVadim Lomovtsev #define CAM_ACCEPT        BIT(3)
37f8ad1f3fSVadim Lomovtsev #define MCAST_MODE_MASK   0x3
38f8ad1f3fSVadim Lomovtsev #define BGX_MCAST_MODE(x) (x << 1)
39f8ad1f3fSVadim Lomovtsev 
403a34ecfdSVadim Lomovtsev struct dmac_map {
413a34ecfdSVadim Lomovtsev 	u64                     vf_map;
423a34ecfdSVadim Lomovtsev 	u64                     dmac;
433a34ecfdSVadim Lomovtsev };
443a34ecfdSVadim Lomovtsev 
454863dea3SSunil Goutham struct lmac {
464863dea3SSunil Goutham 	struct bgx		*bgx;
473a34ecfdSVadim Lomovtsev 	/* actual number of DMACs configured */
483a34ecfdSVadim Lomovtsev 	u8			dmacs_cfg;
493a34ecfdSVadim Lomovtsev 	/* overal number of possible DMACs could be configured per LMAC */
503a34ecfdSVadim Lomovtsev 	u8                      dmacs_count;
513a34ecfdSVadim Lomovtsev 	struct dmac_map         *dmacs; /* DMAC:VFs tracking filter array */
5246b903a0SDavid Daney 	u8			mac[ETH_ALEN];
530bcb7d51SSunil Goutham 	u8                      lmac_type;
540bcb7d51SSunil Goutham 	u8                      lane_to_sds;
550bcb7d51SSunil Goutham 	bool                    use_training;
56075ad765SThanneeru Srinivasulu 	bool                    autoneg;
574863dea3SSunil Goutham 	bool			link_up;
584863dea3SSunil Goutham 	int			lmacid; /* ID within BGX */
594863dea3SSunil Goutham 	int			lmacid_bd; /* ID on board */
604863dea3SSunil Goutham 	struct net_device       netdev;
614863dea3SSunil Goutham 	struct phy_device       *phydev;
624863dea3SSunil Goutham 	unsigned int            last_duplex;
634863dea3SSunil Goutham 	unsigned int            last_link;
644863dea3SSunil Goutham 	unsigned int            last_speed;
654863dea3SSunil Goutham 	bool			is_sgmii;
664863dea3SSunil Goutham 	struct delayed_work	dwork;
674863dea3SSunil Goutham 	struct workqueue_struct *check_link;
680c886a1dSAleksey Makarov };
694863dea3SSunil Goutham 
704863dea3SSunil Goutham struct bgx {
714863dea3SSunil Goutham 	u8			bgx_id;
724863dea3SSunil Goutham 	struct	lmac		lmac[MAX_LMAC_PER_BGX];
737aa48655SVadim Lomovtsev 	u8			lmac_count;
746465859aSSunil Goutham 	u8			max_lmac;
757aa48655SVadim Lomovtsev 	u8                      acpi_lmac_idx;
764863dea3SSunil Goutham 	void __iomem		*reg_base;
774863dea3SSunil Goutham 	struct pci_dev		*pdev;
7809de3917SSunil Goutham 	bool                    is_dlm;
796465859aSSunil Goutham 	bool                    is_rgx;
800c886a1dSAleksey Makarov };
814863dea3SSunil Goutham 
82fd7ec062SAleksey Makarov static struct bgx *bgx_vnic[MAX_BGX_THUNDER];
834863dea3SSunil Goutham static int lmac_count; /* Total no of LMACs in system */
844863dea3SSunil Goutham 
854863dea3SSunil Goutham static int bgx_xaui_check_link(struct lmac *lmac);
864863dea3SSunil Goutham 
874863dea3SSunil Goutham /* Supported devices */
884863dea3SSunil Goutham static const struct pci_device_id bgx_id_table[] = {
894863dea3SSunil Goutham 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_BGX) },
906465859aSSunil Goutham 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_RGX) },
914863dea3SSunil Goutham 	{ 0, }  /* end of table */
924863dea3SSunil Goutham };
934863dea3SSunil Goutham 
944863dea3SSunil Goutham MODULE_AUTHOR("Cavium Inc");
954863dea3SSunil Goutham MODULE_DESCRIPTION("Cavium Thunder BGX/MAC Driver");
964863dea3SSunil Goutham MODULE_LICENSE("GPL v2");
974863dea3SSunil Goutham MODULE_VERSION(DRV_VERSION);
984863dea3SSunil Goutham MODULE_DEVICE_TABLE(pci, bgx_id_table);
994863dea3SSunil Goutham 
1004863dea3SSunil Goutham /* The Cavium ThunderX network controller can *only* be found in SoCs
1014863dea3SSunil Goutham  * containing the ThunderX ARM64 CPU implementation.  All accesses to the device
1024863dea3SSunil Goutham  * registers on this platform are implicitly strongly ordered with respect
1034863dea3SSunil Goutham  * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use
1044863dea3SSunil Goutham  * with no memory barriers in this driver.  The readq()/writeq() functions add
1054863dea3SSunil Goutham  * explicit ordering operation which in this case are redundant, and only
1064863dea3SSunil Goutham  * add overhead.
1074863dea3SSunil Goutham  */
1084863dea3SSunil Goutham 
1094863dea3SSunil Goutham /* Register read/write APIs */
1104863dea3SSunil Goutham static u64 bgx_reg_read(struct bgx *bgx, u8 lmac, u64 offset)
1114863dea3SSunil Goutham {
1124863dea3SSunil Goutham 	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1134863dea3SSunil Goutham 
1144863dea3SSunil Goutham 	return readq_relaxed(addr);
1154863dea3SSunil Goutham }
1164863dea3SSunil Goutham 
1174863dea3SSunil Goutham static void bgx_reg_write(struct bgx *bgx, u8 lmac, u64 offset, u64 val)
1184863dea3SSunil Goutham {
1194863dea3SSunil Goutham 	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1204863dea3SSunil Goutham 
1214863dea3SSunil Goutham 	writeq_relaxed(val, addr);
1224863dea3SSunil Goutham }
1234863dea3SSunil Goutham 
1244863dea3SSunil Goutham static void bgx_reg_modify(struct bgx *bgx, u8 lmac, u64 offset, u64 val)
1254863dea3SSunil Goutham {
1264863dea3SSunil Goutham 	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1274863dea3SSunil Goutham 
1284863dea3SSunil Goutham 	writeq_relaxed(val | readq_relaxed(addr), addr);
1294863dea3SSunil Goutham }
1304863dea3SSunil Goutham 
1314863dea3SSunil Goutham static int bgx_poll_reg(struct bgx *bgx, u8 lmac, u64 reg, u64 mask, bool zero)
1324863dea3SSunil Goutham {
1334863dea3SSunil Goutham 	int timeout = 100;
1344863dea3SSunil Goutham 	u64 reg_val;
1354863dea3SSunil Goutham 
1364863dea3SSunil Goutham 	while (timeout) {
1374863dea3SSunil Goutham 		reg_val = bgx_reg_read(bgx, lmac, reg);
1384863dea3SSunil Goutham 		if (zero && !(reg_val & mask))
1394863dea3SSunil Goutham 			return 0;
1404863dea3SSunil Goutham 		if (!zero && (reg_val & mask))
1414863dea3SSunil Goutham 			return 0;
1424863dea3SSunil Goutham 		usleep_range(1000, 2000);
1434863dea3SSunil Goutham 		timeout--;
1444863dea3SSunil Goutham 	}
1454863dea3SSunil Goutham 	return 1;
1464863dea3SSunil Goutham }
1474863dea3SSunil Goutham 
14878aacb6fSSunil Goutham static int max_bgx_per_node;
14978aacb6fSSunil Goutham static void set_max_bgx_per_node(struct pci_dev *pdev)
15078aacb6fSSunil Goutham {
15178aacb6fSSunil Goutham 	u16 sdevid;
15278aacb6fSSunil Goutham 
15378aacb6fSSunil Goutham 	if (max_bgx_per_node)
15478aacb6fSSunil Goutham 		return;
15578aacb6fSSunil Goutham 
15678aacb6fSSunil Goutham 	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid);
15778aacb6fSSunil Goutham 	switch (sdevid) {
15878aacb6fSSunil Goutham 	case PCI_SUBSYS_DEVID_81XX_BGX:
159b47a57a2SGeorge Cherian 	case PCI_SUBSYS_DEVID_81XX_RGX:
16078aacb6fSSunil Goutham 		max_bgx_per_node = MAX_BGX_PER_CN81XX;
16178aacb6fSSunil Goutham 		break;
16278aacb6fSSunil Goutham 	case PCI_SUBSYS_DEVID_83XX_BGX:
16378aacb6fSSunil Goutham 		max_bgx_per_node = MAX_BGX_PER_CN83XX;
16478aacb6fSSunil Goutham 		break;
16578aacb6fSSunil Goutham 	case PCI_SUBSYS_DEVID_88XX_BGX:
16678aacb6fSSunil Goutham 	default:
16778aacb6fSSunil Goutham 		max_bgx_per_node = MAX_BGX_PER_CN88XX;
16878aacb6fSSunil Goutham 		break;
16978aacb6fSSunil Goutham 	}
17078aacb6fSSunil Goutham }
17178aacb6fSSunil Goutham 
17278aacb6fSSunil Goutham static struct bgx *get_bgx(int node, int bgx_idx)
17378aacb6fSSunil Goutham {
17478aacb6fSSunil Goutham 	int idx = (node * max_bgx_per_node) + bgx_idx;
17578aacb6fSSunil Goutham 
17678aacb6fSSunil Goutham 	return bgx_vnic[idx];
17778aacb6fSSunil Goutham }
17878aacb6fSSunil Goutham 
1794863dea3SSunil Goutham /* Return number of BGX present in HW */
1804863dea3SSunil Goutham unsigned bgx_get_map(int node)
1814863dea3SSunil Goutham {
1824863dea3SSunil Goutham 	int i;
1834863dea3SSunil Goutham 	unsigned map = 0;
1844863dea3SSunil Goutham 
18578aacb6fSSunil Goutham 	for (i = 0; i < max_bgx_per_node; i++) {
18678aacb6fSSunil Goutham 		if (bgx_vnic[(node * max_bgx_per_node) + i])
1874863dea3SSunil Goutham 			map |= (1 << i);
1884863dea3SSunil Goutham 	}
1894863dea3SSunil Goutham 
1904863dea3SSunil Goutham 	return map;
1914863dea3SSunil Goutham }
1924863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_map);
1934863dea3SSunil Goutham 
1944863dea3SSunil Goutham /* Return number of LMAC configured for this BGX */
1954863dea3SSunil Goutham int bgx_get_lmac_count(int node, int bgx_idx)
1964863dea3SSunil Goutham {
1974863dea3SSunil Goutham 	struct bgx *bgx;
1984863dea3SSunil Goutham 
19978aacb6fSSunil Goutham 	bgx = get_bgx(node, bgx_idx);
2004863dea3SSunil Goutham 	if (bgx)
2014863dea3SSunil Goutham 		return bgx->lmac_count;
2024863dea3SSunil Goutham 
2034863dea3SSunil Goutham 	return 0;
2044863dea3SSunil Goutham }
2054863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_lmac_count);
2064863dea3SSunil Goutham 
2074863dea3SSunil Goutham /* Returns the current link status of LMAC */
2084863dea3SSunil Goutham void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status)
2094863dea3SSunil Goutham {
2104863dea3SSunil Goutham 	struct bgx_link_status *link = (struct bgx_link_status *)status;
2114863dea3SSunil Goutham 	struct bgx *bgx;
2124863dea3SSunil Goutham 	struct lmac *lmac;
2134863dea3SSunil Goutham 
21478aacb6fSSunil Goutham 	bgx = get_bgx(node, bgx_idx);
2154863dea3SSunil Goutham 	if (!bgx)
2164863dea3SSunil Goutham 		return;
2174863dea3SSunil Goutham 
2184863dea3SSunil Goutham 	lmac = &bgx->lmac[lmacid];
2191cc70259SThanneeru Srinivasulu 	link->mac_type = lmac->lmac_type;
2204863dea3SSunil Goutham 	link->link_up = lmac->link_up;
2214863dea3SSunil Goutham 	link->duplex = lmac->last_duplex;
2224863dea3SSunil Goutham 	link->speed = lmac->last_speed;
2234863dea3SSunil Goutham }
2244863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_lmac_link_state);
2254863dea3SSunil Goutham 
226e610cb32SAleksey Makarov const u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid)
2274863dea3SSunil Goutham {
22878aacb6fSSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
2294863dea3SSunil Goutham 
2304863dea3SSunil Goutham 	if (bgx)
2314863dea3SSunil Goutham 		return bgx->lmac[lmacid].mac;
2324863dea3SSunil Goutham 
2334863dea3SSunil Goutham 	return NULL;
2344863dea3SSunil Goutham }
2354863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_lmac_mac);
2364863dea3SSunil Goutham 
237e610cb32SAleksey Makarov void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
2384863dea3SSunil Goutham {
23978aacb6fSSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
2404863dea3SSunil Goutham 
2414863dea3SSunil Goutham 	if (!bgx)
2424863dea3SSunil Goutham 		return;
2434863dea3SSunil Goutham 
2444863dea3SSunil Goutham 	ether_addr_copy(bgx->lmac[lmacid].mac, mac);
2454863dea3SSunil Goutham }
2464863dea3SSunil Goutham EXPORT_SYMBOL(bgx_set_lmac_mac);
2474863dea3SSunil Goutham 
2483a34ecfdSVadim Lomovtsev static void bgx_flush_dmac_cam_filter(struct bgx *bgx, int lmacid)
2493a34ecfdSVadim Lomovtsev {
2503a34ecfdSVadim Lomovtsev 	struct lmac *lmac = NULL;
2513a34ecfdSVadim Lomovtsev 	u8  idx = 0;
2523a34ecfdSVadim Lomovtsev 
2533a34ecfdSVadim Lomovtsev 	lmac = &bgx->lmac[lmacid];
2543a34ecfdSVadim Lomovtsev 	/* reset CAM filters */
2553a34ecfdSVadim Lomovtsev 	for (idx = 0; idx < lmac->dmacs_count; idx++)
2563a34ecfdSVadim Lomovtsev 		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
2573a34ecfdSVadim Lomovtsev 			      ((lmacid * lmac->dmacs_count) + idx) *
2583a34ecfdSVadim Lomovtsev 			      sizeof(u64), 0);
2593a34ecfdSVadim Lomovtsev }
2603a34ecfdSVadim Lomovtsev 
261ceb9ea21SVadim Lomovtsev static void bgx_lmac_remove_filters(struct lmac *lmac, u8 vf_id)
262ceb9ea21SVadim Lomovtsev {
263ceb9ea21SVadim Lomovtsev 	int i = 0;
264ceb9ea21SVadim Lomovtsev 
265ceb9ea21SVadim Lomovtsev 	if (!lmac)
266ceb9ea21SVadim Lomovtsev 		return;
267ceb9ea21SVadim Lomovtsev 
268ceb9ea21SVadim Lomovtsev 	/* We've got reset filters request from some of attached VF, while the
269ceb9ea21SVadim Lomovtsev 	 * others might want to keep their configuration. So in this case lets
270ceb9ea21SVadim Lomovtsev 	 * iterate over all of configured filters and decrease number of
271ceb9ea21SVadim Lomovtsev 	 * referencies. if some addresses get zero refs remove them from list
272ceb9ea21SVadim Lomovtsev 	 */
273ceb9ea21SVadim Lomovtsev 	for (i = lmac->dmacs_cfg - 1; i >= 0; i--) {
274ceb9ea21SVadim Lomovtsev 		lmac->dmacs[i].vf_map &= ~BIT_ULL(vf_id);
275ceb9ea21SVadim Lomovtsev 		if (!lmac->dmacs[i].vf_map) {
276ceb9ea21SVadim Lomovtsev 			lmac->dmacs_cfg--;
277ceb9ea21SVadim Lomovtsev 			lmac->dmacs[i].dmac = 0;
278ceb9ea21SVadim Lomovtsev 			lmac->dmacs[i].vf_map = 0;
279ceb9ea21SVadim Lomovtsev 		}
280ceb9ea21SVadim Lomovtsev 	}
281ceb9ea21SVadim Lomovtsev }
282ceb9ea21SVadim Lomovtsev 
283ceb9ea21SVadim Lomovtsev static int bgx_lmac_save_filter(struct lmac *lmac, u64 dmac, u8 vf_id)
284ceb9ea21SVadim Lomovtsev {
285ceb9ea21SVadim Lomovtsev 	u8 i = 0;
286ceb9ea21SVadim Lomovtsev 
287ceb9ea21SVadim Lomovtsev 	if (!lmac)
288ceb9ea21SVadim Lomovtsev 		return -1;
289ceb9ea21SVadim Lomovtsev 
290ceb9ea21SVadim Lomovtsev 	/* At the same time we could have several VFs 'attached' to some
291ceb9ea21SVadim Lomovtsev 	 * particular LMAC, and each VF is represented as network interface
292ceb9ea21SVadim Lomovtsev 	 * for kernel. So from user perspective it should be possible to
293ceb9ea21SVadim Lomovtsev 	 * manipulate with its' (VF) receive modes. However from PF
294ceb9ea21SVadim Lomovtsev 	 * driver perspective we need to keep track of filter configurations
295ceb9ea21SVadim Lomovtsev 	 * for different VFs to prevent filter values dupes
296ceb9ea21SVadim Lomovtsev 	 */
297ceb9ea21SVadim Lomovtsev 	for (i = 0; i < lmac->dmacs_cfg; i++) {
298ceb9ea21SVadim Lomovtsev 		if (lmac->dmacs[i].dmac == dmac) {
299ceb9ea21SVadim Lomovtsev 			lmac->dmacs[i].vf_map |= BIT_ULL(vf_id);
300ceb9ea21SVadim Lomovtsev 			return -1;
301ceb9ea21SVadim Lomovtsev 		}
302ceb9ea21SVadim Lomovtsev 	}
303ceb9ea21SVadim Lomovtsev 
304ceb9ea21SVadim Lomovtsev 	if (!(lmac->dmacs_cfg < lmac->dmacs_count))
305ceb9ea21SVadim Lomovtsev 		return -1;
306ceb9ea21SVadim Lomovtsev 
307ceb9ea21SVadim Lomovtsev 	/* keep it for further tracking */
308ceb9ea21SVadim Lomovtsev 	lmac->dmacs[lmac->dmacs_cfg].dmac = dmac;
309ceb9ea21SVadim Lomovtsev 	lmac->dmacs[lmac->dmacs_cfg].vf_map = BIT_ULL(vf_id);
310ceb9ea21SVadim Lomovtsev 	lmac->dmacs_cfg++;
311ceb9ea21SVadim Lomovtsev 	return 0;
312ceb9ea21SVadim Lomovtsev }
313ceb9ea21SVadim Lomovtsev 
314ceb9ea21SVadim Lomovtsev static int bgx_set_dmac_cam_filter_mac(struct bgx *bgx, int lmacid,
315ceb9ea21SVadim Lomovtsev 				       u64 cam_dmac, u8 idx)
316ceb9ea21SVadim Lomovtsev {
317ceb9ea21SVadim Lomovtsev 	struct lmac *lmac = NULL;
318ceb9ea21SVadim Lomovtsev 	u64 cfg = 0;
319ceb9ea21SVadim Lomovtsev 
320ceb9ea21SVadim Lomovtsev 	/* skip zero addresses as meaningless */
321ceb9ea21SVadim Lomovtsev 	if (!cam_dmac || !bgx)
322ceb9ea21SVadim Lomovtsev 		return -1;
323ceb9ea21SVadim Lomovtsev 
324ceb9ea21SVadim Lomovtsev 	lmac = &bgx->lmac[lmacid];
325ceb9ea21SVadim Lomovtsev 
326ceb9ea21SVadim Lomovtsev 	/* configure DCAM filtering for designated LMAC */
327ceb9ea21SVadim Lomovtsev 	cfg = RX_DMACX_CAM_LMACID(lmacid & LMAC_ID_MASK) |
328ceb9ea21SVadim Lomovtsev 		RX_DMACX_CAM_EN | cam_dmac;
329ceb9ea21SVadim Lomovtsev 	bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
330ceb9ea21SVadim Lomovtsev 		      ((lmacid * lmac->dmacs_count) + idx) * sizeof(u64), cfg);
331ceb9ea21SVadim Lomovtsev 	return 0;
332ceb9ea21SVadim Lomovtsev }
333ceb9ea21SVadim Lomovtsev 
334ceb9ea21SVadim Lomovtsev void bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid,
335ceb9ea21SVadim Lomovtsev 			     u64 cam_dmac, u8 vf_id)
336ceb9ea21SVadim Lomovtsev {
337ceb9ea21SVadim Lomovtsev 	struct bgx *bgx = get_bgx(node, bgx_idx);
338ceb9ea21SVadim Lomovtsev 	struct lmac *lmac = NULL;
339ceb9ea21SVadim Lomovtsev 
340ceb9ea21SVadim Lomovtsev 	if (!bgx)
341ceb9ea21SVadim Lomovtsev 		return;
342ceb9ea21SVadim Lomovtsev 
343ceb9ea21SVadim Lomovtsev 	lmac = &bgx->lmac[lmacid];
344ceb9ea21SVadim Lomovtsev 
345ceb9ea21SVadim Lomovtsev 	if (!cam_dmac)
346ceb9ea21SVadim Lomovtsev 		cam_dmac = ether_addr_to_u64(lmac->mac);
347ceb9ea21SVadim Lomovtsev 
348ceb9ea21SVadim Lomovtsev 	/* since we might have several VFs attached to particular LMAC
349ceb9ea21SVadim Lomovtsev 	 * and kernel could call mcast config for each of them with the
350ceb9ea21SVadim Lomovtsev 	 * same MAC, check if requested MAC is already in filtering list and
351ceb9ea21SVadim Lomovtsev 	 * updare/prepare list of MACs to be applied later to HW filters
352ceb9ea21SVadim Lomovtsev 	 */
353ceb9ea21SVadim Lomovtsev 	bgx_lmac_save_filter(lmac, cam_dmac, vf_id);
354ceb9ea21SVadim Lomovtsev }
355ceb9ea21SVadim Lomovtsev EXPORT_SYMBOL(bgx_set_dmac_cam_filter);
356ceb9ea21SVadim Lomovtsev 
357ceb9ea21SVadim Lomovtsev void bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode)
358ceb9ea21SVadim Lomovtsev {
359ceb9ea21SVadim Lomovtsev 	struct bgx *bgx = get_bgx(node, bgx_idx);
360ceb9ea21SVadim Lomovtsev 	struct lmac *lmac = NULL;
361ceb9ea21SVadim Lomovtsev 	u64 cfg = 0;
362ceb9ea21SVadim Lomovtsev 	u8 i = 0;
363ceb9ea21SVadim Lomovtsev 
364ceb9ea21SVadim Lomovtsev 	if (!bgx)
365ceb9ea21SVadim Lomovtsev 		return;
366ceb9ea21SVadim Lomovtsev 
367ceb9ea21SVadim Lomovtsev 	lmac = &bgx->lmac[lmacid];
368ceb9ea21SVadim Lomovtsev 
369ceb9ea21SVadim Lomovtsev 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL);
370ceb9ea21SVadim Lomovtsev 	if (mode & BGX_XCAST_BCAST_ACCEPT)
371ceb9ea21SVadim Lomovtsev 		cfg |= BCAST_ACCEPT;
372ceb9ea21SVadim Lomovtsev 	else
373ceb9ea21SVadim Lomovtsev 		cfg &= ~BCAST_ACCEPT;
374ceb9ea21SVadim Lomovtsev 
375ceb9ea21SVadim Lomovtsev 	/* disable all MCASTs and DMAC filtering */
376ceb9ea21SVadim Lomovtsev 	cfg &= ~(CAM_ACCEPT | BGX_MCAST_MODE(MCAST_MODE_MASK));
377ceb9ea21SVadim Lomovtsev 
378ceb9ea21SVadim Lomovtsev 	/* check requested bits and set filtergin mode appropriately */
379ceb9ea21SVadim Lomovtsev 	if (mode & (BGX_XCAST_MCAST_ACCEPT)) {
380ceb9ea21SVadim Lomovtsev 		cfg |= (BGX_MCAST_MODE(MCAST_MODE_ACCEPT));
381ceb9ea21SVadim Lomovtsev 	} else if (mode & BGX_XCAST_MCAST_FILTER) {
382ceb9ea21SVadim Lomovtsev 		cfg |= (BGX_MCAST_MODE(MCAST_MODE_CAM_FILTER) | CAM_ACCEPT);
383ceb9ea21SVadim Lomovtsev 		for (i = 0; i < lmac->dmacs_cfg; i++)
384ceb9ea21SVadim Lomovtsev 			bgx_set_dmac_cam_filter_mac(bgx, lmacid,
385ceb9ea21SVadim Lomovtsev 						    lmac->dmacs[i].dmac, i);
386ceb9ea21SVadim Lomovtsev 	}
387ceb9ea21SVadim Lomovtsev 	bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, cfg);
388ceb9ea21SVadim Lomovtsev }
389ceb9ea21SVadim Lomovtsev EXPORT_SYMBOL(bgx_set_xcast_mode);
390ceb9ea21SVadim Lomovtsev 
391ceb9ea21SVadim Lomovtsev void bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf_id)
392ceb9ea21SVadim Lomovtsev {
393ceb9ea21SVadim Lomovtsev 	struct bgx *bgx = get_bgx(node, bgx_idx);
394ceb9ea21SVadim Lomovtsev 
395ceb9ea21SVadim Lomovtsev 	if (!bgx)
396ceb9ea21SVadim Lomovtsev 		return;
397ceb9ea21SVadim Lomovtsev 
398ceb9ea21SVadim Lomovtsev 	bgx_lmac_remove_filters(&bgx->lmac[lmacid], vf_id);
399ceb9ea21SVadim Lomovtsev 	bgx_flush_dmac_cam_filter(bgx, lmacid);
400ceb9ea21SVadim Lomovtsev 	bgx_set_xcast_mode(node, bgx_idx, lmacid,
401ceb9ea21SVadim Lomovtsev 			   (BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT));
402ceb9ea21SVadim Lomovtsev }
403ceb9ea21SVadim Lomovtsev EXPORT_SYMBOL(bgx_reset_xcast_mode);
404ceb9ea21SVadim Lomovtsev 
405bc69fdfcSSunil Goutham void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
406bc69fdfcSSunil Goutham {
40778aacb6fSSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
4086465859aSSunil Goutham 	struct lmac *lmac;
409bc69fdfcSSunil Goutham 	u64 cfg;
410bc69fdfcSSunil Goutham 
411bc69fdfcSSunil Goutham 	if (!bgx)
412bc69fdfcSSunil Goutham 		return;
4136465859aSSunil Goutham 	lmac = &bgx->lmac[lmacid];
414bc69fdfcSSunil Goutham 
415bc69fdfcSSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
416bc69fdfcSSunil Goutham 	if (enable)
417bc69fdfcSSunil Goutham 		cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN;
418bc69fdfcSSunil Goutham 	else
419bc69fdfcSSunil Goutham 		cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
420bc69fdfcSSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
4216465859aSSunil Goutham 
4226465859aSSunil Goutham 	if (bgx->is_rgx)
4236465859aSSunil Goutham 		xcv_setup_link(enable ? lmac->link_up : 0, lmac->last_speed);
424bc69fdfcSSunil Goutham }
425bc69fdfcSSunil Goutham EXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
426bc69fdfcSSunil Goutham 
4274a875509SSunil Goutham /* Enables or disables timestamp insertion by BGX for Rx packets */
4284a875509SSunil Goutham void bgx_config_timestamping(int node, int bgx_idx, int lmacid, bool enable)
4294a875509SSunil Goutham {
4304a875509SSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
4314a875509SSunil Goutham 	struct lmac *lmac;
4324a875509SSunil Goutham 	u64 csr_offset, cfg;
4334a875509SSunil Goutham 
4344a875509SSunil Goutham 	if (!bgx)
4354a875509SSunil Goutham 		return;
4364a875509SSunil Goutham 
4374a875509SSunil Goutham 	lmac = &bgx->lmac[lmacid];
4384a875509SSunil Goutham 
4394a875509SSunil Goutham 	if (lmac->lmac_type == BGX_MODE_SGMII ||
4404a875509SSunil Goutham 	    lmac->lmac_type == BGX_MODE_QSGMII ||
4414a875509SSunil Goutham 	    lmac->lmac_type == BGX_MODE_RGMII)
4424a875509SSunil Goutham 		csr_offset = BGX_GMP_GMI_RXX_FRM_CTL;
4434a875509SSunil Goutham 	else
4444a875509SSunil Goutham 		csr_offset = BGX_SMUX_RX_FRM_CTL;
4454a875509SSunil Goutham 
4464a875509SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, csr_offset);
4474a875509SSunil Goutham 
4484a875509SSunil Goutham 	if (enable)
4494a875509SSunil Goutham 		cfg |= BGX_PKT_RX_PTP_EN;
4504a875509SSunil Goutham 	else
4514a875509SSunil Goutham 		cfg &= ~BGX_PKT_RX_PTP_EN;
4524a875509SSunil Goutham 	bgx_reg_write(bgx, lmacid, csr_offset, cfg);
4534a875509SSunil Goutham }
4544a875509SSunil Goutham EXPORT_SYMBOL(bgx_config_timestamping);
4554a875509SSunil Goutham 
456430da208SSunil Goutham void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause)
457430da208SSunil Goutham {
458430da208SSunil Goutham 	struct pfc *pfc = (struct pfc *)pause;
45978aacb6fSSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
460430da208SSunil Goutham 	struct lmac *lmac;
461430da208SSunil Goutham 	u64 cfg;
462430da208SSunil Goutham 
463430da208SSunil Goutham 	if (!bgx)
464430da208SSunil Goutham 		return;
465430da208SSunil Goutham 	lmac = &bgx->lmac[lmacid];
466430da208SSunil Goutham 	if (lmac->is_sgmii)
467430da208SSunil Goutham 		return;
468430da208SSunil Goutham 
469430da208SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
470430da208SSunil Goutham 	pfc->fc_rx = cfg & RX_EN;
471430da208SSunil Goutham 	pfc->fc_tx = cfg & TX_EN;
472430da208SSunil Goutham 	pfc->autoneg = 0;
473430da208SSunil Goutham }
474430da208SSunil Goutham EXPORT_SYMBOL(bgx_lmac_get_pfc);
475430da208SSunil Goutham 
476430da208SSunil Goutham void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause)
477430da208SSunil Goutham {
478430da208SSunil Goutham 	struct pfc *pfc = (struct pfc *)pause;
47978aacb6fSSunil Goutham 	struct bgx *bgx = get_bgx(node, bgx_idx);
480430da208SSunil Goutham 	struct lmac *lmac;
481430da208SSunil Goutham 	u64 cfg;
482430da208SSunil Goutham 
483430da208SSunil Goutham 	if (!bgx)
484430da208SSunil Goutham 		return;
485430da208SSunil Goutham 	lmac = &bgx->lmac[lmacid];
486430da208SSunil Goutham 	if (lmac->is_sgmii)
487430da208SSunil Goutham 		return;
488430da208SSunil Goutham 
489430da208SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
490430da208SSunil Goutham 	cfg &= ~(RX_EN | TX_EN);
491430da208SSunil Goutham 	cfg |= (pfc->fc_rx ? RX_EN : 0x00);
492430da208SSunil Goutham 	cfg |= (pfc->fc_tx ? TX_EN : 0x00);
493430da208SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg);
494430da208SSunil Goutham }
495430da208SSunil Goutham EXPORT_SYMBOL(bgx_lmac_set_pfc);
496430da208SSunil Goutham 
4974863dea3SSunil Goutham static void bgx_sgmii_change_link_state(struct lmac *lmac)
4984863dea3SSunil Goutham {
4994863dea3SSunil Goutham 	struct bgx *bgx = lmac->bgx;
5004863dea3SSunil Goutham 	u64 cmr_cfg;
5014863dea3SSunil Goutham 	u64 port_cfg = 0;
5024863dea3SSunil Goutham 	u64 misc_ctl = 0;
503500268e9SSunil Goutham 	bool tx_en, rx_en;
5044863dea3SSunil Goutham 
5054863dea3SSunil Goutham 	cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG);
506500268e9SSunil Goutham 	tx_en = cmr_cfg & CMR_PKT_TX_EN;
507500268e9SSunil Goutham 	rx_en = cmr_cfg & CMR_PKT_RX_EN;
508500268e9SSunil Goutham 	cmr_cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
5094863dea3SSunil Goutham 	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
5104863dea3SSunil Goutham 
511500268e9SSunil Goutham 	/* Wait for BGX RX to be idle */
512500268e9SSunil Goutham 	if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG,
513500268e9SSunil Goutham 			 GMI_PORT_CFG_RX_IDLE, false)) {
514500268e9SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI RX not idle\n",
515500268e9SSunil Goutham 			bgx->bgx_id, lmac->lmacid);
516500268e9SSunil Goutham 		return;
517500268e9SSunil Goutham 	}
518500268e9SSunil Goutham 
519500268e9SSunil Goutham 	/* Wait for BGX TX to be idle */
520500268e9SSunil Goutham 	if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG,
521500268e9SSunil Goutham 			 GMI_PORT_CFG_TX_IDLE, false)) {
522500268e9SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI TX not idle\n",
523500268e9SSunil Goutham 			bgx->bgx_id, lmac->lmacid);
524500268e9SSunil Goutham 		return;
525500268e9SSunil Goutham 	}
526500268e9SSunil Goutham 
5274863dea3SSunil Goutham 	port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG);
5284863dea3SSunil Goutham 	misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL);
5294863dea3SSunil Goutham 
5304863dea3SSunil Goutham 	if (lmac->link_up) {
5314863dea3SSunil Goutham 		misc_ctl &= ~PCS_MISC_CTL_GMX_ENO;
5324863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_DUPLEX;
5334863dea3SSunil Goutham 		port_cfg |=  (lmac->last_duplex << 2);
5344863dea3SSunil Goutham 	} else {
5354863dea3SSunil Goutham 		misc_ctl |= PCS_MISC_CTL_GMX_ENO;
5364863dea3SSunil Goutham 	}
5374863dea3SSunil Goutham 
5384863dea3SSunil Goutham 	switch (lmac->last_speed) {
5394863dea3SSunil Goutham 	case 10:
5404863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
5414863dea3SSunil Goutham 		port_cfg |= GMI_PORT_CFG_SPEED_MSB;  /* speed_msb 1 */
5424863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
5434863dea3SSunil Goutham 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5444863dea3SSunil Goutham 		misc_ctl |= 50; /* samp_pt */
5454863dea3SSunil Goutham 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
5464863dea3SSunil Goutham 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
5474863dea3SSunil Goutham 		break;
5484863dea3SSunil Goutham 	case 100:
5494863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
5504863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
5514863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
5524863dea3SSunil Goutham 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5534863dea3SSunil Goutham 		misc_ctl |= 5; /* samp_pt */
5544863dea3SSunil Goutham 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
5554863dea3SSunil Goutham 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
5564863dea3SSunil Goutham 		break;
5574863dea3SSunil Goutham 	case 1000:
5584863dea3SSunil Goutham 		port_cfg |= GMI_PORT_CFG_SPEED; /* speed 1 */
5594863dea3SSunil Goutham 		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
5604863dea3SSunil Goutham 		port_cfg |= GMI_PORT_CFG_SLOT_TIME; /* slottime 1 */
5614863dea3SSunil Goutham 		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5624863dea3SSunil Goutham 		misc_ctl |= 1; /* samp_pt */
5634863dea3SSunil Goutham 		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 512);
5644863dea3SSunil Goutham 		if (lmac->last_duplex)
5654863dea3SSunil Goutham 			bgx_reg_write(bgx, lmac->lmacid,
5664863dea3SSunil Goutham 				      BGX_GMP_GMI_TXX_BURST, 0);
5674863dea3SSunil Goutham 		else
5684863dea3SSunil Goutham 			bgx_reg_write(bgx, lmac->lmacid,
5694863dea3SSunil Goutham 				      BGX_GMP_GMI_TXX_BURST, 8192);
5704863dea3SSunil Goutham 		break;
5714863dea3SSunil Goutham 	default:
5724863dea3SSunil Goutham 		break;
5734863dea3SSunil Goutham 	}
5744863dea3SSunil Goutham 	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl);
5754863dea3SSunil Goutham 	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg);
5764863dea3SSunil Goutham 
577500268e9SSunil Goutham 	/* Restore CMR config settings */
578500268e9SSunil Goutham 	cmr_cfg |= (rx_en ? CMR_PKT_RX_EN : 0) | (tx_en ? CMR_PKT_TX_EN : 0);
5794863dea3SSunil Goutham 	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
5806465859aSSunil Goutham 
5816465859aSSunil Goutham 	if (bgx->is_rgx && (cmr_cfg & (CMR_PKT_RX_EN | CMR_PKT_TX_EN)))
5826465859aSSunil Goutham 		xcv_setup_link(lmac->link_up, lmac->last_speed);
5834863dea3SSunil Goutham }
5844863dea3SSunil Goutham 
585fd7ec062SAleksey Makarov static void bgx_lmac_handler(struct net_device *netdev)
5864863dea3SSunil Goutham {
5874863dea3SSunil Goutham 	struct lmac *lmac = container_of(netdev, struct lmac, netdev);
588099a728dSxypron.glpk@gmx.de 	struct phy_device *phydev;
5894863dea3SSunil Goutham 	int link_changed = 0;
5904863dea3SSunil Goutham 
5914863dea3SSunil Goutham 	if (!lmac)
5924863dea3SSunil Goutham 		return;
5934863dea3SSunil Goutham 
594099a728dSxypron.glpk@gmx.de 	phydev = lmac->phydev;
595099a728dSxypron.glpk@gmx.de 
5964863dea3SSunil Goutham 	if (!phydev->link && lmac->last_link)
5974863dea3SSunil Goutham 		link_changed = -1;
5984863dea3SSunil Goutham 
5994863dea3SSunil Goutham 	if (phydev->link &&
6004863dea3SSunil Goutham 	    (lmac->last_duplex != phydev->duplex ||
6014863dea3SSunil Goutham 	     lmac->last_link != phydev->link ||
6024863dea3SSunil Goutham 	     lmac->last_speed != phydev->speed)) {
6034863dea3SSunil Goutham 			link_changed = 1;
6044863dea3SSunil Goutham 	}
6054863dea3SSunil Goutham 
6064863dea3SSunil Goutham 	lmac->last_link = phydev->link;
6074863dea3SSunil Goutham 	lmac->last_speed = phydev->speed;
6084863dea3SSunil Goutham 	lmac->last_duplex = phydev->duplex;
6094863dea3SSunil Goutham 
6104863dea3SSunil Goutham 	if (!link_changed)
6114863dea3SSunil Goutham 		return;
6124863dea3SSunil Goutham 
6134863dea3SSunil Goutham 	if (link_changed > 0)
6144863dea3SSunil Goutham 		lmac->link_up = true;
6154863dea3SSunil Goutham 	else
6164863dea3SSunil Goutham 		lmac->link_up = false;
6174863dea3SSunil Goutham 
6184863dea3SSunil Goutham 	if (lmac->is_sgmii)
6194863dea3SSunil Goutham 		bgx_sgmii_change_link_state(lmac);
6204863dea3SSunil Goutham 	else
6214863dea3SSunil Goutham 		bgx_xaui_check_link(lmac);
6224863dea3SSunil Goutham }
6234863dea3SSunil Goutham 
6244863dea3SSunil Goutham u64 bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx)
6254863dea3SSunil Goutham {
6264863dea3SSunil Goutham 	struct bgx *bgx;
6274863dea3SSunil Goutham 
62878aacb6fSSunil Goutham 	bgx = get_bgx(node, bgx_idx);
6294863dea3SSunil Goutham 	if (!bgx)
6304863dea3SSunil Goutham 		return 0;
6314863dea3SSunil Goutham 
6324863dea3SSunil Goutham 	if (idx > 8)
6334863dea3SSunil Goutham 		lmac = 0;
6344863dea3SSunil Goutham 	return bgx_reg_read(bgx, lmac, BGX_CMRX_RX_STAT0 + (idx * 8));
6354863dea3SSunil Goutham }
6364863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_rx_stats);
6374863dea3SSunil Goutham 
6384863dea3SSunil Goutham u64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx)
6394863dea3SSunil Goutham {
6404863dea3SSunil Goutham 	struct bgx *bgx;
6414863dea3SSunil Goutham 
64278aacb6fSSunil Goutham 	bgx = get_bgx(node, bgx_idx);
6434863dea3SSunil Goutham 	if (!bgx)
6444863dea3SSunil Goutham 		return 0;
6454863dea3SSunil Goutham 
6464863dea3SSunil Goutham 	return bgx_reg_read(bgx, lmac, BGX_CMRX_TX_STAT0 + (idx * 8));
6474863dea3SSunil Goutham }
6484863dea3SSunil Goutham EXPORT_SYMBOL(bgx_get_tx_stats);
6494863dea3SSunil Goutham 
650d77a2384SSunil Goutham /* Configure BGX LMAC in internal loopback mode */
651d77a2384SSunil Goutham void bgx_lmac_internal_loopback(int node, int bgx_idx,
652d77a2384SSunil Goutham 				int lmac_idx, bool enable)
653d77a2384SSunil Goutham {
654d77a2384SSunil Goutham 	struct bgx *bgx;
655d77a2384SSunil Goutham 	struct lmac *lmac;
656d77a2384SSunil Goutham 	u64    cfg;
657d77a2384SSunil Goutham 
65878aacb6fSSunil Goutham 	bgx = get_bgx(node, bgx_idx);
659d77a2384SSunil Goutham 	if (!bgx)
660d77a2384SSunil Goutham 		return;
661d77a2384SSunil Goutham 
662d77a2384SSunil Goutham 	lmac = &bgx->lmac[lmac_idx];
663d77a2384SSunil Goutham 	if (lmac->is_sgmii) {
664d77a2384SSunil Goutham 		cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL);
665d77a2384SSunil Goutham 		if (enable)
666d77a2384SSunil Goutham 			cfg |= PCS_MRX_CTL_LOOPBACK1;
667d77a2384SSunil Goutham 		else
668d77a2384SSunil Goutham 			cfg &= ~PCS_MRX_CTL_LOOPBACK1;
669d77a2384SSunil Goutham 		bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg);
670d77a2384SSunil Goutham 	} else {
671d77a2384SSunil Goutham 		cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1);
672d77a2384SSunil Goutham 		if (enable)
673d77a2384SSunil Goutham 			cfg |= SPU_CTL_LOOPBACK;
674d77a2384SSunil Goutham 		else
675d77a2384SSunil Goutham 			cfg &= ~SPU_CTL_LOOPBACK;
676d77a2384SSunil Goutham 		bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg);
677d77a2384SSunil Goutham 	}
678d77a2384SSunil Goutham }
679d77a2384SSunil Goutham EXPORT_SYMBOL(bgx_lmac_internal_loopback);
680d77a2384SSunil Goutham 
6813f8057cfSSunil Goutham static int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac)
6824863dea3SSunil Goutham {
6833f8057cfSSunil Goutham 	int lmacid = lmac->lmacid;
6844863dea3SSunil Goutham 	u64 cfg;
6854863dea3SSunil Goutham 
6864863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30);
6874863dea3SSunil Goutham 	/* max packet size */
6884863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE);
6894863dea3SSunil Goutham 
6904863dea3SSunil Goutham 	/* Disable frame alignment if using preamble */
6914863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
6924863dea3SSunil Goutham 	if (cfg & 1)
6934863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0);
6944863dea3SSunil Goutham 
6954863dea3SSunil Goutham 	/* Enable lmac */
6964863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
6974863dea3SSunil Goutham 
6984863dea3SSunil Goutham 	/* PCS reset */
6994863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET);
7004863dea3SSunil Goutham 	if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL,
7014863dea3SSunil Goutham 			 PCS_MRX_CTL_RESET, true)) {
7024863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX PCS reset not completed\n");
7034863dea3SSunil Goutham 		return -1;
7044863dea3SSunil Goutham 	}
7054863dea3SSunil Goutham 
7064863dea3SSunil Goutham 	/* power down, reset autoneg, autoneg enable */
7074863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
7084863dea3SSunil Goutham 	cfg &= ~PCS_MRX_CTL_PWR_DN;
709075ad765SThanneeru Srinivasulu 	cfg |= PCS_MRX_CTL_RST_AN;
710075ad765SThanneeru Srinivasulu 	if (lmac->phydev) {
711075ad765SThanneeru Srinivasulu 		cfg |= PCS_MRX_CTL_AN_EN;
712075ad765SThanneeru Srinivasulu 	} else {
713075ad765SThanneeru Srinivasulu 		/* In scenarios where PHY driver is not present or it's a
714075ad765SThanneeru Srinivasulu 		 * non-standard PHY, FW sets AN_EN to inform Linux driver
715075ad765SThanneeru Srinivasulu 		 * to do auto-neg and link polling or not.
716075ad765SThanneeru Srinivasulu 		 */
717075ad765SThanneeru Srinivasulu 		if (cfg & PCS_MRX_CTL_AN_EN)
718075ad765SThanneeru Srinivasulu 			lmac->autoneg = true;
719075ad765SThanneeru Srinivasulu 	}
7204863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
7214863dea3SSunil Goutham 
7223f8057cfSSunil Goutham 	if (lmac->lmac_type == BGX_MODE_QSGMII) {
7233f8057cfSSunil Goutham 		/* Disable disparity check for QSGMII */
7243f8057cfSSunil Goutham 		cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL);
7253f8057cfSSunil Goutham 		cfg &= ~PCS_MISC_CTL_DISP_EN;
7263f8057cfSSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg);
7273f8057cfSSunil Goutham 		return 0;
7283f8057cfSSunil Goutham 	}
7293f8057cfSSunil Goutham 
730075ad765SThanneeru Srinivasulu 	if ((lmac->lmac_type == BGX_MODE_SGMII) && lmac->phydev) {
7314863dea3SSunil Goutham 		if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS,
7324863dea3SSunil Goutham 				 PCS_MRX_STATUS_AN_CPT, false)) {
7334863dea3SSunil Goutham 			dev_err(&bgx->pdev->dev, "BGX AN_CPT not completed\n");
7344863dea3SSunil Goutham 			return -1;
7354863dea3SSunil Goutham 		}
7366465859aSSunil Goutham 	}
7374863dea3SSunil Goutham 
7384863dea3SSunil Goutham 	return 0;
7394863dea3SSunil Goutham }
7404863dea3SSunil Goutham 
7410bcb7d51SSunil Goutham static int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac)
7424863dea3SSunil Goutham {
7434863dea3SSunil Goutham 	u64 cfg;
7440bcb7d51SSunil Goutham 	int lmacid = lmac->lmacid;
7454863dea3SSunil Goutham 
7464863dea3SSunil Goutham 	/* Reset SPU */
7474863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET);
7484863dea3SSunil Goutham 	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
7494863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n");
7504863dea3SSunil Goutham 		return -1;
7514863dea3SSunil Goutham 	}
7524863dea3SSunil Goutham 
7534863dea3SSunil Goutham 	/* Disable LMAC */
7544863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
7554863dea3SSunil Goutham 	cfg &= ~CMR_EN;
7564863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
7574863dea3SSunil Goutham 
7584863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
7594863dea3SSunil Goutham 	/* Set interleaved running disparity for RXAUI */
76093db2cf8SSunil Goutham 	if (lmac->lmac_type == BGX_MODE_RXAUI)
7614863dea3SSunil Goutham 		bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL,
76293db2cf8SSunil Goutham 			       SPU_MISC_CTL_INTLV_RDISP);
76393db2cf8SSunil Goutham 
76493db2cf8SSunil Goutham 	/* Clear receive packet disable */
76593db2cf8SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL);
76693db2cf8SSunil Goutham 	cfg &= ~SPU_MISC_CTL_RX_DIS;
76793db2cf8SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg);
7684863dea3SSunil Goutham 
7694863dea3SSunil Goutham 	/* clear all interrupts */
7704863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT);
7714863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg);
7724863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT);
7734863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg);
7744863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
7754863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
7764863dea3SSunil Goutham 
7770bcb7d51SSunil Goutham 	if (lmac->use_training) {
7784863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00);
7794863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00);
7804863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00);
7814863dea3SSunil Goutham 		/* training enable */
7824863dea3SSunil Goutham 		bgx_reg_modify(bgx, lmacid,
7834863dea3SSunil Goutham 			       BGX_SPUX_BR_PMD_CRTL, SPU_PMD_CRTL_TRAIN_EN);
7844863dea3SSunil Goutham 	}
7854863dea3SSunil Goutham 
7864863dea3SSunil Goutham 	/* Append FCS to each packet */
7874863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D);
7884863dea3SSunil Goutham 
7894863dea3SSunil Goutham 	/* Disable forward error correction */
7904863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL);
7914863dea3SSunil Goutham 	cfg &= ~SPU_FEC_CTL_FEC_EN;
7924863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg);
7934863dea3SSunil Goutham 
7944863dea3SSunil Goutham 	/* Disable autoneg */
7954863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL);
7964863dea3SSunil Goutham 	cfg = cfg & ~(SPU_AN_CTL_AN_EN | SPU_AN_CTL_XNP_EN);
7974863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg);
7984863dea3SSunil Goutham 
7994863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV);
8000bcb7d51SSunil Goutham 	if (lmac->lmac_type == BGX_MODE_10G_KR)
8014863dea3SSunil Goutham 		cfg |= (1 << 23);
8020bcb7d51SSunil Goutham 	else if (lmac->lmac_type == BGX_MODE_40G_KR)
8034863dea3SSunil Goutham 		cfg |= (1 << 24);
8044863dea3SSunil Goutham 	else
8054863dea3SSunil Goutham 		cfg &= ~((1 << 23) | (1 << 24));
8064863dea3SSunil Goutham 	cfg = cfg & (~((1ULL << 25) | (1ULL << 22) | (1ULL << 12)));
8074863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg);
8084863dea3SSunil Goutham 
8094863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL);
8104863dea3SSunil Goutham 	cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN;
8114863dea3SSunil Goutham 	bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg);
8124863dea3SSunil Goutham 
8134863dea3SSunil Goutham 	/* Enable lmac */
8144863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
8154863dea3SSunil Goutham 
8164863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1);
8174863dea3SSunil Goutham 	cfg &= ~SPU_CTL_LOW_POWER;
8184863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg);
8194863dea3SSunil Goutham 
8204863dea3SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL);
8214863dea3SSunil Goutham 	cfg &= ~SMU_TX_CTL_UNI_EN;
8224863dea3SSunil Goutham 	cfg |= SMU_TX_CTL_DIC_EN;
8234863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
8244863dea3SSunil Goutham 
825430da208SSunil Goutham 	/* Enable receive and transmission of pause frames */
826430da208SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) |
827430da208SSunil Goutham 		      BCK_EN | DRP_EN | TX_EN | RX_EN));
828430da208SSunil Goutham 	/* Configure pause time and interval */
829430da208SSunil Goutham 	bgx_reg_write(bgx, lmacid,
830430da208SSunil Goutham 		      BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME);
831430da208SSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL);
832430da208SSunil Goutham 	cfg &= ~0xFFFFull;
833430da208SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL,
834430da208SSunil Goutham 		      cfg | (DEFAULT_PAUSE_TIME - 0x1000));
835430da208SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01);
836430da208SSunil Goutham 
8374863dea3SSunil Goutham 	/* take lmac_count into account */
8384863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
8394863dea3SSunil Goutham 	/* max packet size */
8404863dea3SSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE);
8414863dea3SSunil Goutham 
8424863dea3SSunil Goutham 	return 0;
8434863dea3SSunil Goutham }
8444863dea3SSunil Goutham 
8454863dea3SSunil Goutham static int bgx_xaui_check_link(struct lmac *lmac)
8464863dea3SSunil Goutham {
8474863dea3SSunil Goutham 	struct bgx *bgx = lmac->bgx;
8484863dea3SSunil Goutham 	int lmacid = lmac->lmacid;
8490bcb7d51SSunil Goutham 	int lmac_type = lmac->lmac_type;
8504863dea3SSunil Goutham 	u64 cfg;
8514863dea3SSunil Goutham 
8520bcb7d51SSunil Goutham 	if (lmac->use_training) {
8534863dea3SSunil Goutham 		cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
8544863dea3SSunil Goutham 		if (!(cfg & (1ull << 13))) {
8554863dea3SSunil Goutham 			cfg = (1ull << 13) | (1ull << 14);
8564863dea3SSunil Goutham 			bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
8574863dea3SSunil Goutham 			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL);
8584863dea3SSunil Goutham 			cfg |= (1ull << 0);
8594863dea3SSunil Goutham 			bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg);
8604863dea3SSunil Goutham 			return -1;
8614863dea3SSunil Goutham 		}
8624863dea3SSunil Goutham 	}
8634863dea3SSunil Goutham 
8644863dea3SSunil Goutham 	/* wait for PCS to come out of reset */
8654863dea3SSunil Goutham 	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
8664863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n");
8674863dea3SSunil Goutham 		return -1;
8684863dea3SSunil Goutham 	}
8694863dea3SSunil Goutham 
8704863dea3SSunil Goutham 	if ((lmac_type == BGX_MODE_10G_KR) || (lmac_type == BGX_MODE_XFI) ||
8714863dea3SSunil Goutham 	    (lmac_type == BGX_MODE_40G_KR) || (lmac_type == BGX_MODE_XLAUI)) {
8724863dea3SSunil Goutham 		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1,
8734863dea3SSunil Goutham 				 SPU_BR_STATUS_BLK_LOCK, false)) {
8744863dea3SSunil Goutham 			dev_err(&bgx->pdev->dev,
8754863dea3SSunil Goutham 				"SPU_BR_STATUS_BLK_LOCK not completed\n");
8764863dea3SSunil Goutham 			return -1;
8774863dea3SSunil Goutham 		}
8784863dea3SSunil Goutham 	} else {
8794863dea3SSunil Goutham 		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS,
8804863dea3SSunil Goutham 				 SPU_BX_STATUS_RX_ALIGN, false)) {
8814863dea3SSunil Goutham 			dev_err(&bgx->pdev->dev,
8824863dea3SSunil Goutham 				"SPU_BX_STATUS_RX_ALIGN not completed\n");
8834863dea3SSunil Goutham 			return -1;
8844863dea3SSunil Goutham 		}
8854863dea3SSunil Goutham 	}
8864863dea3SSunil Goutham 
8874863dea3SSunil Goutham 	/* Clear rcvflt bit (latching high) and read it back */
8883f4c68cfSSunil Goutham 	if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT)
8893f4c68cfSSunil Goutham 		bgx_reg_modify(bgx, lmacid,
8903f4c68cfSSunil Goutham 			       BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
8914863dea3SSunil Goutham 	if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
8924863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "Receive fault, retry training\n");
8930bcb7d51SSunil Goutham 		if (lmac->use_training) {
8944863dea3SSunil Goutham 			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
8954863dea3SSunil Goutham 			if (!(cfg & (1ull << 13))) {
8964863dea3SSunil Goutham 				cfg = (1ull << 13) | (1ull << 14);
8974863dea3SSunil Goutham 				bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
8984863dea3SSunil Goutham 				cfg = bgx_reg_read(bgx, lmacid,
8994863dea3SSunil Goutham 						   BGX_SPUX_BR_PMD_CRTL);
9004863dea3SSunil Goutham 				cfg |= (1ull << 0);
9014863dea3SSunil Goutham 				bgx_reg_write(bgx, lmacid,
9024863dea3SSunil Goutham 					      BGX_SPUX_BR_PMD_CRTL, cfg);
9034863dea3SSunil Goutham 				return -1;
9044863dea3SSunil Goutham 			}
9054863dea3SSunil Goutham 		}
9064863dea3SSunil Goutham 		return -1;
9074863dea3SSunil Goutham 	}
9084863dea3SSunil Goutham 
9094863dea3SSunil Goutham 	/* Wait for BGX RX to be idle */
9104863dea3SSunil Goutham 	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) {
9114863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "SMU RX not idle\n");
9124863dea3SSunil Goutham 		return -1;
9134863dea3SSunil Goutham 	}
9144863dea3SSunil Goutham 
9154863dea3SSunil Goutham 	/* Wait for BGX TX to be idle */
9164863dea3SSunil Goutham 	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_TX_IDLE, false)) {
9174863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "SMU TX not idle\n");
9184863dea3SSunil Goutham 		return -1;
9194863dea3SSunil Goutham 	}
9204863dea3SSunil Goutham 
9213f4c68cfSSunil Goutham 	/* Check for MAC RX faults */
9223f4c68cfSSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_CTL);
9233f4c68cfSSunil Goutham 	/* 0 - Link is okay, 1 - Local fault, 2 - Remote fault */
9243f4c68cfSSunil Goutham 	cfg &= SMU_RX_CTL_STATUS;
9253f4c68cfSSunil Goutham 	if (!cfg)
9264863dea3SSunil Goutham 		return 0;
9273f4c68cfSSunil Goutham 
9283f4c68cfSSunil Goutham 	/* Rx local/remote fault seen.
9293f4c68cfSSunil Goutham 	 * Do lmac reinit to see if condition recovers
9303f4c68cfSSunil Goutham 	 */
9310bcb7d51SSunil Goutham 	bgx_lmac_xaui_init(bgx, lmac);
9323f4c68cfSSunil Goutham 
9333f4c68cfSSunil Goutham 	return -1;
9344863dea3SSunil Goutham }
9354863dea3SSunil Goutham 
936075ad765SThanneeru Srinivasulu static void bgx_poll_for_sgmii_link(struct lmac *lmac)
937075ad765SThanneeru Srinivasulu {
938075ad765SThanneeru Srinivasulu 	u64 pcs_link, an_result;
939075ad765SThanneeru Srinivasulu 	u8 speed;
940075ad765SThanneeru Srinivasulu 
941075ad765SThanneeru Srinivasulu 	pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
942075ad765SThanneeru Srinivasulu 				BGX_GMP_PCS_MRX_STATUS);
943075ad765SThanneeru Srinivasulu 
944075ad765SThanneeru Srinivasulu 	/*Link state bit is sticky, read it again*/
945075ad765SThanneeru Srinivasulu 	if (!(pcs_link & PCS_MRX_STATUS_LINK))
946075ad765SThanneeru Srinivasulu 		pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
947075ad765SThanneeru Srinivasulu 					BGX_GMP_PCS_MRX_STATUS);
948075ad765SThanneeru Srinivasulu 
949075ad765SThanneeru Srinivasulu 	if (bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_GMP_PCS_MRX_STATUS,
950075ad765SThanneeru Srinivasulu 			 PCS_MRX_STATUS_AN_CPT, false)) {
951075ad765SThanneeru Srinivasulu 		lmac->link_up = false;
952075ad765SThanneeru Srinivasulu 		lmac->last_speed = SPEED_UNKNOWN;
953075ad765SThanneeru Srinivasulu 		lmac->last_duplex = DUPLEX_UNKNOWN;
954075ad765SThanneeru Srinivasulu 		goto next_poll;
955075ad765SThanneeru Srinivasulu 	}
956075ad765SThanneeru Srinivasulu 
957075ad765SThanneeru Srinivasulu 	lmac->link_up = ((pcs_link & PCS_MRX_STATUS_LINK) != 0) ? true : false;
958075ad765SThanneeru Srinivasulu 	an_result = bgx_reg_read(lmac->bgx, lmac->lmacid,
959075ad765SThanneeru Srinivasulu 				 BGX_GMP_PCS_ANX_AN_RESULTS);
960075ad765SThanneeru Srinivasulu 
961075ad765SThanneeru Srinivasulu 	speed = (an_result >> 3) & 0x3;
962075ad765SThanneeru Srinivasulu 	lmac->last_duplex = (an_result >> 1) & 0x1;
963075ad765SThanneeru Srinivasulu 	switch (speed) {
964075ad765SThanneeru Srinivasulu 	case 0:
965075ad765SThanneeru Srinivasulu 		lmac->last_speed = 10;
966075ad765SThanneeru Srinivasulu 		break;
967075ad765SThanneeru Srinivasulu 	case 1:
968075ad765SThanneeru Srinivasulu 		lmac->last_speed = 100;
969075ad765SThanneeru Srinivasulu 		break;
970075ad765SThanneeru Srinivasulu 	case 2:
971075ad765SThanneeru Srinivasulu 		lmac->last_speed = 1000;
972075ad765SThanneeru Srinivasulu 		break;
973075ad765SThanneeru Srinivasulu 	default:
974075ad765SThanneeru Srinivasulu 		lmac->link_up = false;
975075ad765SThanneeru Srinivasulu 		lmac->last_speed = SPEED_UNKNOWN;
976075ad765SThanneeru Srinivasulu 		lmac->last_duplex = DUPLEX_UNKNOWN;
977075ad765SThanneeru Srinivasulu 		break;
978075ad765SThanneeru Srinivasulu 	}
979075ad765SThanneeru Srinivasulu 
980075ad765SThanneeru Srinivasulu next_poll:
981075ad765SThanneeru Srinivasulu 
982075ad765SThanneeru Srinivasulu 	if (lmac->last_link != lmac->link_up) {
983075ad765SThanneeru Srinivasulu 		if (lmac->link_up)
984075ad765SThanneeru Srinivasulu 			bgx_sgmii_change_link_state(lmac);
985075ad765SThanneeru Srinivasulu 		lmac->last_link = lmac->link_up;
986075ad765SThanneeru Srinivasulu 	}
987075ad765SThanneeru Srinivasulu 
988075ad765SThanneeru Srinivasulu 	queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 3);
989075ad765SThanneeru Srinivasulu }
990075ad765SThanneeru Srinivasulu 
9914863dea3SSunil Goutham static void bgx_poll_for_link(struct work_struct *work)
9924863dea3SSunil Goutham {
9934863dea3SSunil Goutham 	struct lmac *lmac;
9943f4c68cfSSunil Goutham 	u64 spu_link, smu_link;
9954863dea3SSunil Goutham 
9964863dea3SSunil Goutham 	lmac = container_of(work, struct lmac, dwork.work);
997075ad765SThanneeru Srinivasulu 	if (lmac->is_sgmii) {
998075ad765SThanneeru Srinivasulu 		bgx_poll_for_sgmii_link(lmac);
999075ad765SThanneeru Srinivasulu 		return;
1000075ad765SThanneeru Srinivasulu 	}
10014863dea3SSunil Goutham 
10024863dea3SSunil Goutham 	/* Receive link is latching low. Force it high and verify it */
10034863dea3SSunil Goutham 	bgx_reg_modify(lmac->bgx, lmac->lmacid,
10044863dea3SSunil Goutham 		       BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK);
10054863dea3SSunil Goutham 	bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1,
10064863dea3SSunil Goutham 		     SPU_STATUS1_RCV_LNK, false);
10074863dea3SSunil Goutham 
10083f4c68cfSSunil Goutham 	spu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1);
10093f4c68cfSSunil Goutham 	smu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL);
10103f4c68cfSSunil Goutham 
10113f4c68cfSSunil Goutham 	if ((spu_link & SPU_STATUS1_RCV_LNK) &&
10123f4c68cfSSunil Goutham 	    !(smu_link & SMU_RX_CTL_STATUS)) {
10134863dea3SSunil Goutham 		lmac->link_up = 1;
10140bcb7d51SSunil Goutham 		if (lmac->lmac_type == BGX_MODE_XLAUI)
10154863dea3SSunil Goutham 			lmac->last_speed = 40000;
10164863dea3SSunil Goutham 		else
10174863dea3SSunil Goutham 			lmac->last_speed = 10000;
10184863dea3SSunil Goutham 		lmac->last_duplex = 1;
10194863dea3SSunil Goutham 	} else {
10204863dea3SSunil Goutham 		lmac->link_up = 0;
10210b72a9a1SSunil Goutham 		lmac->last_speed = SPEED_UNKNOWN;
10220b72a9a1SSunil Goutham 		lmac->last_duplex = DUPLEX_UNKNOWN;
10234863dea3SSunil Goutham 	}
10244863dea3SSunil Goutham 
10254863dea3SSunil Goutham 	if (lmac->last_link != lmac->link_up) {
10263f4c68cfSSunil Goutham 		if (lmac->link_up) {
10273f4c68cfSSunil Goutham 			if (bgx_xaui_check_link(lmac)) {
10283f4c68cfSSunil Goutham 				/* Errors, clear link_up state */
10293f4c68cfSSunil Goutham 				lmac->link_up = 0;
10303f4c68cfSSunil Goutham 				lmac->last_speed = SPEED_UNKNOWN;
10313f4c68cfSSunil Goutham 				lmac->last_duplex = DUPLEX_UNKNOWN;
10323f4c68cfSSunil Goutham 			}
10333f4c68cfSSunil Goutham 		}
10344863dea3SSunil Goutham 		lmac->last_link = lmac->link_up;
10354863dea3SSunil Goutham 	}
10364863dea3SSunil Goutham 
10374863dea3SSunil Goutham 	queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 2);
10384863dea3SSunil Goutham }
10394863dea3SSunil Goutham 
10403f8057cfSSunil Goutham static int phy_interface_mode(u8 lmac_type)
10413f8057cfSSunil Goutham {
10423f8057cfSSunil Goutham 	if (lmac_type == BGX_MODE_QSGMII)
10433f8057cfSSunil Goutham 		return PHY_INTERFACE_MODE_QSGMII;
10446465859aSSunil Goutham 	if (lmac_type == BGX_MODE_RGMII)
10456465859aSSunil Goutham 		return PHY_INTERFACE_MODE_RGMII;
10463f8057cfSSunil Goutham 
10473f8057cfSSunil Goutham 	return PHY_INTERFACE_MODE_SGMII;
10483f8057cfSSunil Goutham }
10493f8057cfSSunil Goutham 
10504863dea3SSunil Goutham static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
10514863dea3SSunil Goutham {
10524863dea3SSunil Goutham 	struct lmac *lmac;
10534863dea3SSunil Goutham 	u64 cfg;
10544863dea3SSunil Goutham 
10554863dea3SSunil Goutham 	lmac = &bgx->lmac[lmacid];
10564863dea3SSunil Goutham 	lmac->bgx = bgx;
10574863dea3SSunil Goutham 
10583f8057cfSSunil Goutham 	if ((lmac->lmac_type == BGX_MODE_SGMII) ||
10596465859aSSunil Goutham 	    (lmac->lmac_type == BGX_MODE_QSGMII) ||
10606465859aSSunil Goutham 	    (lmac->lmac_type == BGX_MODE_RGMII)) {
10614863dea3SSunil Goutham 		lmac->is_sgmii = 1;
10623f8057cfSSunil Goutham 		if (bgx_lmac_sgmii_init(bgx, lmac))
10634863dea3SSunil Goutham 			return -1;
10644863dea3SSunil Goutham 	} else {
10654863dea3SSunil Goutham 		lmac->is_sgmii = 0;
10660bcb7d51SSunil Goutham 		if (bgx_lmac_xaui_init(bgx, lmac))
10674863dea3SSunil Goutham 			return -1;
10684863dea3SSunil Goutham 	}
10694863dea3SSunil Goutham 
10704863dea3SSunil Goutham 	if (lmac->is_sgmii) {
10714863dea3SSunil Goutham 		cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
10724863dea3SSunil Goutham 		cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
10734863dea3SSunil Goutham 		bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg);
10744863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1);
10754863dea3SSunil Goutham 	} else {
10764863dea3SSunil Goutham 		cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND);
10774863dea3SSunil Goutham 		cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
10784863dea3SSunil Goutham 		bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg);
10794863dea3SSunil Goutham 		bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
10804863dea3SSunil Goutham 	}
10814863dea3SSunil Goutham 
10823a34ecfdSVadim Lomovtsev 	/* actual number of filters available to exact LMAC */
10833a34ecfdSVadim Lomovtsev 	lmac->dmacs_count = (RX_DMAC_COUNT / bgx->lmac_count);
10843a34ecfdSVadim Lomovtsev 	lmac->dmacs = kcalloc(lmac->dmacs_count, sizeof(*lmac->dmacs),
10853a34ecfdSVadim Lomovtsev 			      GFP_KERNEL);
1086a94cead7SColin Ian King 	if (!lmac->dmacs)
1087a94cead7SColin Ian King 		return -ENOMEM;
10883a34ecfdSVadim Lomovtsev 
10894863dea3SSunil Goutham 	/* Enable lmac */
1090bc69fdfcSSunil Goutham 	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
10914863dea3SSunil Goutham 
10924863dea3SSunil Goutham 	/* Restore default cfg, incase low level firmware changed it */
10934863dea3SSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03);
10944863dea3SSunil Goutham 
10950bcb7d51SSunil Goutham 	if ((lmac->lmac_type != BGX_MODE_XFI) &&
10960bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_XLAUI) &&
10970bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_40G_KR) &&
10980bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_10G_KR)) {
1099075ad765SThanneeru Srinivasulu 		if (!lmac->phydev) {
1100075ad765SThanneeru Srinivasulu 			if (lmac->autoneg) {
1101075ad765SThanneeru Srinivasulu 				bgx_reg_write(bgx, lmacid,
1102075ad765SThanneeru Srinivasulu 					      BGX_GMP_PCS_LINKX_TIMER,
1103075ad765SThanneeru Srinivasulu 					      PCS_LINKX_TIMER_COUNT);
1104075ad765SThanneeru Srinivasulu 				goto poll;
1105075ad765SThanneeru Srinivasulu 			} else {
1106075ad765SThanneeru Srinivasulu 				/* Default to below link speed and duplex */
1107075ad765SThanneeru Srinivasulu 				lmac->link_up = true;
1108075ad765SThanneeru Srinivasulu 				lmac->last_speed = 1000;
1109075ad765SThanneeru Srinivasulu 				lmac->last_duplex = 1;
1110075ad765SThanneeru Srinivasulu 				bgx_sgmii_change_link_state(lmac);
1111075ad765SThanneeru Srinivasulu 				return 0;
1112075ad765SThanneeru Srinivasulu 			}
1113075ad765SThanneeru Srinivasulu 		}
11144863dea3SSunil Goutham 		lmac->phydev->dev_flags = 0;
11154863dea3SSunil Goutham 
11164863dea3SSunil Goutham 		if (phy_connect_direct(&lmac->netdev, lmac->phydev,
11174863dea3SSunil Goutham 				       bgx_lmac_handler,
11183f8057cfSSunil Goutham 				       phy_interface_mode(lmac->lmac_type)))
11194863dea3SSunil Goutham 			return -ENODEV;
11204863dea3SSunil Goutham 
11214863dea3SSunil Goutham 		phy_start_aneg(lmac->phydev);
1122075ad765SThanneeru Srinivasulu 		return 0;
1123075ad765SThanneeru Srinivasulu 	}
1124075ad765SThanneeru Srinivasulu 
1125075ad765SThanneeru Srinivasulu poll:
11264863dea3SSunil Goutham 	lmac->check_link = alloc_workqueue("check_link", WQ_UNBOUND |
11274863dea3SSunil Goutham 					   WQ_MEM_RECLAIM, 1);
11284863dea3SSunil Goutham 	if (!lmac->check_link)
11294863dea3SSunil Goutham 		return -ENOMEM;
11304863dea3SSunil Goutham 	INIT_DELAYED_WORK(&lmac->dwork, bgx_poll_for_link);
11314863dea3SSunil Goutham 	queue_delayed_work(lmac->check_link, &lmac->dwork, 0);
11324863dea3SSunil Goutham 
11334863dea3SSunil Goutham 	return 0;
11344863dea3SSunil Goutham }
11354863dea3SSunil Goutham 
1136fd7ec062SAleksey Makarov static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
11374863dea3SSunil Goutham {
11384863dea3SSunil Goutham 	struct lmac *lmac;
11393f4c68cfSSunil Goutham 	u64 cfg;
11404863dea3SSunil Goutham 
11414863dea3SSunil Goutham 	lmac = &bgx->lmac[lmacid];
11424863dea3SSunil Goutham 	if (lmac->check_link) {
11434863dea3SSunil Goutham 		/* Destroy work queue */
1144a7b1f535SThanneeru Srinivasulu 		cancel_delayed_work_sync(&lmac->dwork);
11454863dea3SSunil Goutham 		destroy_workqueue(lmac->check_link);
11464863dea3SSunil Goutham 	}
11474863dea3SSunil Goutham 
11483f4c68cfSSunil Goutham 	/* Disable packet reception */
11493f4c68cfSSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11503f4c68cfSSunil Goutham 	cfg &= ~CMR_PKT_RX_EN;
11513f4c68cfSSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11523f4c68cfSSunil Goutham 
11533f4c68cfSSunil Goutham 	/* Give chance for Rx/Tx FIFO to get drained */
11543f4c68cfSSunil Goutham 	bgx_poll_reg(bgx, lmacid, BGX_CMRX_RX_FIFO_LEN, (u64)0x1FFF, true);
11553f4c68cfSSunil Goutham 	bgx_poll_reg(bgx, lmacid, BGX_CMRX_TX_FIFO_LEN, (u64)0x3FFF, true);
11563f4c68cfSSunil Goutham 
11573f4c68cfSSunil Goutham 	/* Disable packet transmission */
11583f4c68cfSSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11593f4c68cfSSunil Goutham 	cfg &= ~CMR_PKT_TX_EN;
11603f4c68cfSSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11613f4c68cfSSunil Goutham 
11623f4c68cfSSunil Goutham 	/* Disable serdes lanes */
11633f4c68cfSSunil Goutham         if (!lmac->is_sgmii)
11643f4c68cfSSunil Goutham                 bgx_reg_modify(bgx, lmacid,
11653f4c68cfSSunil Goutham                                BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
11663f4c68cfSSunil Goutham         else
11673f4c68cfSSunil Goutham                 bgx_reg_modify(bgx, lmacid,
11683f4c68cfSSunil Goutham                                BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_PWR_DN);
11693f4c68cfSSunil Goutham 
11703f4c68cfSSunil Goutham 	/* Disable LMAC */
11713f4c68cfSSunil Goutham 	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11723f4c68cfSSunil Goutham 	cfg &= ~CMR_EN;
11733f4c68cfSSunil Goutham 	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11743f4c68cfSSunil Goutham 
11753a34ecfdSVadim Lomovtsev 	bgx_flush_dmac_cam_filter(bgx, lmacid);
11763a34ecfdSVadim Lomovtsev 	kfree(lmac->dmacs);
11774863dea3SSunil Goutham 
11780bcb7d51SSunil Goutham 	if ((lmac->lmac_type != BGX_MODE_XFI) &&
11790bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_XLAUI) &&
11800bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_40G_KR) &&
11810bcb7d51SSunil Goutham 	    (lmac->lmac_type != BGX_MODE_10G_KR) && lmac->phydev)
11824863dea3SSunil Goutham 		phy_disconnect(lmac->phydev);
11834863dea3SSunil Goutham 
11844863dea3SSunil Goutham 	lmac->phydev = NULL;
11854863dea3SSunil Goutham }
11864863dea3SSunil Goutham 
11874863dea3SSunil Goutham static void bgx_init_hw(struct bgx *bgx)
11884863dea3SSunil Goutham {
11894863dea3SSunil Goutham 	int i;
11900bcb7d51SSunil Goutham 	struct lmac *lmac;
11914863dea3SSunil Goutham 
11924863dea3SSunil Goutham 	bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP);
11934863dea3SSunil Goutham 	if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS))
11944863dea3SSunil Goutham 		dev_err(&bgx->pdev->dev, "BGX%d BIST failed\n", bgx->bgx_id);
11954863dea3SSunil Goutham 
11964863dea3SSunil Goutham 	/* Set lmac type and lane2serdes mapping */
11974863dea3SSunil Goutham 	for (i = 0; i < bgx->lmac_count; i++) {
11980bcb7d51SSunil Goutham 		lmac = &bgx->lmac[i];
11994863dea3SSunil Goutham 		bgx_reg_write(bgx, i, BGX_CMRX_CFG,
12000bcb7d51SSunil Goutham 			      (lmac->lmac_type << 8) | lmac->lane_to_sds);
12014863dea3SSunil Goutham 		bgx->lmac[i].lmacid_bd = lmac_count;
12024863dea3SSunil Goutham 		lmac_count++;
12034863dea3SSunil Goutham 	}
12044863dea3SSunil Goutham 
12054863dea3SSunil Goutham 	bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, bgx->lmac_count);
12064863dea3SSunil Goutham 	bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, bgx->lmac_count);
12074863dea3SSunil Goutham 
12084863dea3SSunil Goutham 	/* Set the backpressure AND mask */
12094863dea3SSunil Goutham 	for (i = 0; i < bgx->lmac_count; i++)
12104863dea3SSunil Goutham 		bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND,
12114863dea3SSunil Goutham 			       ((1ULL << MAX_BGX_CHANS_PER_LMAC) - 1) <<
12124863dea3SSunil Goutham 			       (i * MAX_BGX_CHANS_PER_LMAC));
12134863dea3SSunil Goutham 
12144863dea3SSunil Goutham 	/* Disable all MAC filtering */
12154863dea3SSunil Goutham 	for (i = 0; i < RX_DMAC_COUNT; i++)
12164863dea3SSunil Goutham 		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00);
12174863dea3SSunil Goutham 
12184863dea3SSunil Goutham 	/* Disable MAC steering (NCSI traffic) */
12194863dea3SSunil Goutham 	for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++)
1220f6d25acaSVadim Lomovtsev 		bgx_reg_write(bgx, 0, BGX_CMR_RX_STEERING + (i * 8), 0x00);
12214863dea3SSunil Goutham }
12224863dea3SSunil Goutham 
12233f8057cfSSunil Goutham static u8 bgx_get_lane2sds_cfg(struct bgx *bgx, struct lmac *lmac)
12243f8057cfSSunil Goutham {
12253f8057cfSSunil Goutham 	return (u8)(bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG) & 0xFF);
12263f8057cfSSunil Goutham }
12273f8057cfSSunil Goutham 
12280bcb7d51SSunil Goutham static void bgx_print_qlm_mode(struct bgx *bgx, u8 lmacid)
12294863dea3SSunil Goutham {
12304863dea3SSunil Goutham 	struct device *dev = &bgx->pdev->dev;
12310bcb7d51SSunil Goutham 	struct lmac *lmac;
1232c41626ceSArnd Bergmann 	char str[27];
123357aaf63cSSunil Goutham 
1234fff37fdaSSunil Goutham 	if (!bgx->is_dlm && lmacid)
123557aaf63cSSunil Goutham 		return;
12360bcb7d51SSunil Goutham 
12370bcb7d51SSunil Goutham 	lmac = &bgx->lmac[lmacid];
123809de3917SSunil Goutham 	if (!bgx->is_dlm)
12390bcb7d51SSunil Goutham 		sprintf(str, "BGX%d QLM mode", bgx->bgx_id);
124057aaf63cSSunil Goutham 	else
1241fff37fdaSSunil Goutham 		sprintf(str, "BGX%d LMAC%d mode", bgx->bgx_id, lmacid);
12420bcb7d51SSunil Goutham 
12430bcb7d51SSunil Goutham 	switch (lmac->lmac_type) {
12440bcb7d51SSunil Goutham 	case BGX_MODE_SGMII:
12450bcb7d51SSunil Goutham 		dev_info(dev, "%s: SGMII\n", (char *)str);
12460bcb7d51SSunil Goutham 		break;
12470bcb7d51SSunil Goutham 	case BGX_MODE_XAUI:
12480bcb7d51SSunil Goutham 		dev_info(dev, "%s: XAUI\n", (char *)str);
12490bcb7d51SSunil Goutham 		break;
12500bcb7d51SSunil Goutham 	case BGX_MODE_RXAUI:
12510bcb7d51SSunil Goutham 		dev_info(dev, "%s: RXAUI\n", (char *)str);
12520bcb7d51SSunil Goutham 		break;
12530bcb7d51SSunil Goutham 	case BGX_MODE_XFI:
12540bcb7d51SSunil Goutham 		if (!lmac->use_training)
12550bcb7d51SSunil Goutham 			dev_info(dev, "%s: XFI\n", (char *)str);
12560bcb7d51SSunil Goutham 		else
12570bcb7d51SSunil Goutham 			dev_info(dev, "%s: 10G_KR\n", (char *)str);
12580bcb7d51SSunil Goutham 		break;
12590bcb7d51SSunil Goutham 	case BGX_MODE_XLAUI:
12600bcb7d51SSunil Goutham 		if (!lmac->use_training)
12610bcb7d51SSunil Goutham 			dev_info(dev, "%s: XLAUI\n", (char *)str);
12620bcb7d51SSunil Goutham 		else
12630bcb7d51SSunil Goutham 			dev_info(dev, "%s: 40G_KR4\n", (char *)str);
12640bcb7d51SSunil Goutham 		break;
12653f8057cfSSunil Goutham 	case BGX_MODE_QSGMII:
12663f8057cfSSunil Goutham 		dev_info(dev, "%s: QSGMII\n", (char *)str);
12673f8057cfSSunil Goutham 		break;
12686465859aSSunil Goutham 	case BGX_MODE_RGMII:
12696465859aSSunil Goutham 		dev_info(dev, "%s: RGMII\n", (char *)str);
12706465859aSSunil Goutham 		break;
12713f8057cfSSunil Goutham 	case BGX_MODE_INVALID:
12723f8057cfSSunil Goutham 		/* Nothing to do */
12733f8057cfSSunil Goutham 		break;
12740bcb7d51SSunil Goutham 	}
12750bcb7d51SSunil Goutham }
12760bcb7d51SSunil Goutham 
12773f8057cfSSunil Goutham static void lmac_set_lane2sds(struct bgx *bgx, struct lmac *lmac)
12780bcb7d51SSunil Goutham {
12790bcb7d51SSunil Goutham 	switch (lmac->lmac_type) {
12800bcb7d51SSunil Goutham 	case BGX_MODE_SGMII:
12810bcb7d51SSunil Goutham 	case BGX_MODE_XFI:
12820bcb7d51SSunil Goutham 		lmac->lane_to_sds = lmac->lmacid;
12830bcb7d51SSunil Goutham 		break;
12840bcb7d51SSunil Goutham 	case BGX_MODE_XAUI:
12850bcb7d51SSunil Goutham 	case BGX_MODE_XLAUI:
12866465859aSSunil Goutham 	case BGX_MODE_RGMII:
12870bcb7d51SSunil Goutham 		lmac->lane_to_sds = 0xE4;
12880bcb7d51SSunil Goutham 		break;
12890bcb7d51SSunil Goutham 	case BGX_MODE_RXAUI:
12900bcb7d51SSunil Goutham 		lmac->lane_to_sds = (lmac->lmacid) ? 0xE : 0x4;
12910bcb7d51SSunil Goutham 		break;
12923f8057cfSSunil Goutham 	case BGX_MODE_QSGMII:
12933f8057cfSSunil Goutham 		/* There is no way to determine if DLM0/2 is QSGMII or
12943f8057cfSSunil Goutham 		 * DLM1/3 is configured to QSGMII as bootloader will
12953f8057cfSSunil Goutham 		 * configure all LMACs, so take whatever is configured
12963f8057cfSSunil Goutham 		 * by low level firmware.
12973f8057cfSSunil Goutham 		 */
12983f8057cfSSunil Goutham 		lmac->lane_to_sds = bgx_get_lane2sds_cfg(bgx, lmac);
12993f8057cfSSunil Goutham 		break;
13000bcb7d51SSunil Goutham 	default:
13010bcb7d51SSunil Goutham 		lmac->lane_to_sds = 0;
13020bcb7d51SSunil Goutham 		break;
13030bcb7d51SSunil Goutham 	}
13040bcb7d51SSunil Goutham }
13050bcb7d51SSunil Goutham 
13066465859aSSunil Goutham static void lmac_set_training(struct bgx *bgx, struct lmac *lmac, int lmacid)
13076465859aSSunil Goutham {
13086465859aSSunil Goutham 	if ((lmac->lmac_type != BGX_MODE_10G_KR) &&
13096465859aSSunil Goutham 	    (lmac->lmac_type != BGX_MODE_40G_KR)) {
13106465859aSSunil Goutham 		lmac->use_training = 0;
13116465859aSSunil Goutham 		return;
13126465859aSSunil Goutham 	}
13136465859aSSunil Goutham 
13146465859aSSunil Goutham 	lmac->use_training = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL) &
13156465859aSSunil Goutham 							SPU_PMD_CRTL_TRAIN_EN;
13166465859aSSunil Goutham }
13176465859aSSunil Goutham 
13180bcb7d51SSunil Goutham static void bgx_set_lmac_config(struct bgx *bgx, u8 idx)
13190bcb7d51SSunil Goutham {
13200bcb7d51SSunil Goutham 	struct lmac *lmac;
13210bcb7d51SSunil Goutham 	u64 cmr_cfg;
132257aaf63cSSunil Goutham 	u8 lmac_type;
132357aaf63cSSunil Goutham 	u8 lane_to_sds;
13240bcb7d51SSunil Goutham 
13250bcb7d51SSunil Goutham 	lmac = &bgx->lmac[idx];
13264863dea3SSunil Goutham 
132709de3917SSunil Goutham 	if (!bgx->is_dlm || bgx->is_rgx) {
13284863dea3SSunil Goutham 		/* Read LMAC0 type to figure out QLM mode
13294863dea3SSunil Goutham 		 * This is configured by low level firmware
13304863dea3SSunil Goutham 		 */
13310bcb7d51SSunil Goutham 		cmr_cfg = bgx_reg_read(bgx, 0, BGX_CMRX_CFG);
13320bcb7d51SSunil Goutham 		lmac->lmac_type = (cmr_cfg >> 8) & 0x07;
13336465859aSSunil Goutham 		if (bgx->is_rgx)
13346465859aSSunil Goutham 			lmac->lmac_type = BGX_MODE_RGMII;
13356465859aSSunil Goutham 		lmac_set_training(bgx, lmac, 0);
13363f8057cfSSunil Goutham 		lmac_set_lane2sds(bgx, lmac);
133757aaf63cSSunil Goutham 		return;
133857aaf63cSSunil Goutham 	}
133957aaf63cSSunil Goutham 
1340fff37fdaSSunil Goutham 	/* For DLMs or SLMs on 80/81/83xx so many lane configurations
1341fff37fdaSSunil Goutham 	 * are possible and vary across boards. Also Kernel doesn't have
1342fff37fdaSSunil Goutham 	 * any way to identify board type/info and since firmware does,
1343fff37fdaSSunil Goutham 	 * just take lmac type and serdes lane config as is.
134457aaf63cSSunil Goutham 	 */
134557aaf63cSSunil Goutham 	cmr_cfg = bgx_reg_read(bgx, idx, BGX_CMRX_CFG);
134657aaf63cSSunil Goutham 	lmac_type = (u8)((cmr_cfg >> 8) & 0x07);
134757aaf63cSSunil Goutham 	lane_to_sds = (u8)(cmr_cfg & 0xFF);
1348fff37fdaSSunil Goutham 	/* Check if config is reset value */
134957aaf63cSSunil Goutham 	if ((lmac_type == 0) && (lane_to_sds == 0xE4))
135057aaf63cSSunil Goutham 		lmac->lmac_type = BGX_MODE_INVALID;
135157aaf63cSSunil Goutham 	else
135257aaf63cSSunil Goutham 		lmac->lmac_type = lmac_type;
1353fff37fdaSSunil Goutham 	lmac->lane_to_sds = lane_to_sds;
13546465859aSSunil Goutham 	lmac_set_training(bgx, lmac, lmac->lmacid);
13550bcb7d51SSunil Goutham }
13564863dea3SSunil Goutham 
13570bcb7d51SSunil Goutham static void bgx_get_qlm_mode(struct bgx *bgx)
13580bcb7d51SSunil Goutham {
135957aaf63cSSunil Goutham 	struct lmac *lmac;
13600bcb7d51SSunil Goutham 	u8  idx;
13610bcb7d51SSunil Goutham 
136257aaf63cSSunil Goutham 	/* Init all LMAC's type to invalid */
13636465859aSSunil Goutham 	for (idx = 0; idx < bgx->max_lmac; idx++) {
136457aaf63cSSunil Goutham 		lmac = &bgx->lmac[idx];
136557aaf63cSSunil Goutham 		lmac->lmacid = idx;
13666465859aSSunil Goutham 		lmac->lmac_type = BGX_MODE_INVALID;
13676465859aSSunil Goutham 		lmac->use_training = false;
136857aaf63cSSunil Goutham 	}
136957aaf63cSSunil Goutham 
13700bcb7d51SSunil Goutham 	/* It is assumed that low level firmware sets this value */
13710bcb7d51SSunil Goutham 	bgx->lmac_count = bgx_reg_read(bgx, 0, BGX_CMR_RX_LMACS) & 0x7;
13726465859aSSunil Goutham 	if (bgx->lmac_count > bgx->max_lmac)
13736465859aSSunil Goutham 		bgx->lmac_count = bgx->max_lmac;
13740bcb7d51SSunil Goutham 
137557aaf63cSSunil Goutham 	for (idx = 0; idx < bgx->lmac_count; idx++) {
1376fff37fdaSSunil Goutham 		bgx_set_lmac_config(bgx, idx);
1377fff37fdaSSunil Goutham 		bgx_print_qlm_mode(bgx, idx);
137857aaf63cSSunil Goutham 	}
13794863dea3SSunil Goutham }
13804863dea3SSunil Goutham 
138146b903a0SDavid Daney #ifdef CONFIG_ACPI
138246b903a0SDavid Daney 
13831d82efacSRobert Richter static int acpi_get_mac_address(struct device *dev, struct acpi_device *adev,
13841d82efacSRobert Richter 				u8 *dst)
138546b903a0SDavid Daney {
138646b903a0SDavid Daney 	u8 mac[ETH_ALEN];
138746b903a0SDavid Daney 	int ret;
138846b903a0SDavid Daney 
138946b903a0SDavid Daney 	ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
139046b903a0SDavid Daney 					    "mac-address", mac, ETH_ALEN);
139146b903a0SDavid Daney 	if (ret)
139246b903a0SDavid Daney 		goto out;
139346b903a0SDavid Daney 
139446b903a0SDavid Daney 	if (!is_valid_ether_addr(mac)) {
13951d82efacSRobert Richter 		dev_err(dev, "MAC address invalid: %pM\n", mac);
139646b903a0SDavid Daney 		ret = -EINVAL;
139746b903a0SDavid Daney 		goto out;
139846b903a0SDavid Daney 	}
139946b903a0SDavid Daney 
14001d82efacSRobert Richter 	dev_info(dev, "MAC address set to: %pM\n", mac);
14011d82efacSRobert Richter 
140246b903a0SDavid Daney 	memcpy(dst, mac, ETH_ALEN);
140346b903a0SDavid Daney out:
140446b903a0SDavid Daney 	return ret;
140546b903a0SDavid Daney }
140646b903a0SDavid Daney 
140746b903a0SDavid Daney /* Currently only sets the MAC address. */
140846b903a0SDavid Daney static acpi_status bgx_acpi_register_phy(acpi_handle handle,
140946b903a0SDavid Daney 					 u32 lvl, void *context, void **rv)
141046b903a0SDavid Daney {
141146b903a0SDavid Daney 	struct bgx *bgx = context;
14121d82efacSRobert Richter 	struct device *dev = &bgx->pdev->dev;
141346b903a0SDavid Daney 	struct acpi_device *adev;
141446b903a0SDavid Daney 
141546b903a0SDavid Daney 	if (acpi_bus_get_device(handle, &adev))
141646b903a0SDavid Daney 		goto out;
141746b903a0SDavid Daney 
14187aa48655SVadim Lomovtsev 	acpi_get_mac_address(dev, adev, bgx->lmac[bgx->acpi_lmac_idx].mac);
141946b903a0SDavid Daney 
14207aa48655SVadim Lomovtsev 	SET_NETDEV_DEV(&bgx->lmac[bgx->acpi_lmac_idx].netdev, dev);
142146b903a0SDavid Daney 
14227aa48655SVadim Lomovtsev 	bgx->lmac[bgx->acpi_lmac_idx].lmacid = bgx->acpi_lmac_idx;
14237aa48655SVadim Lomovtsev 	bgx->acpi_lmac_idx++; /* move to next LMAC */
142446b903a0SDavid Daney out:
142546b903a0SDavid Daney 	return AE_OK;
142646b903a0SDavid Daney }
142746b903a0SDavid Daney 
142846b903a0SDavid Daney static acpi_status bgx_acpi_match_id(acpi_handle handle, u32 lvl,
142946b903a0SDavid Daney 				     void *context, void **ret_val)
143046b903a0SDavid Daney {
143146b903a0SDavid Daney 	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
143246b903a0SDavid Daney 	struct bgx *bgx = context;
143346b903a0SDavid Daney 	char bgx_sel[5];
143446b903a0SDavid Daney 
143546b903a0SDavid Daney 	snprintf(bgx_sel, 5, "BGX%d", bgx->bgx_id);
143646b903a0SDavid Daney 	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &string))) {
143746b903a0SDavid Daney 		pr_warn("Invalid link device\n");
143846b903a0SDavid Daney 		return AE_OK;
143946b903a0SDavid Daney 	}
144046b903a0SDavid Daney 
144146b903a0SDavid Daney 	if (strncmp(string.pointer, bgx_sel, 4))
144246b903a0SDavid Daney 		return AE_OK;
144346b903a0SDavid Daney 
144446b903a0SDavid Daney 	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
144546b903a0SDavid Daney 			    bgx_acpi_register_phy, NULL, bgx, NULL);
144646b903a0SDavid Daney 
144746b903a0SDavid Daney 	kfree(string.pointer);
144846b903a0SDavid Daney 	return AE_CTRL_TERMINATE;
144946b903a0SDavid Daney }
145046b903a0SDavid Daney 
145146b903a0SDavid Daney static int bgx_init_acpi_phy(struct bgx *bgx)
145246b903a0SDavid Daney {
145346b903a0SDavid Daney 	acpi_get_devices(NULL, bgx_acpi_match_id, bgx, (void **)NULL);
145446b903a0SDavid Daney 	return 0;
145546b903a0SDavid Daney }
145646b903a0SDavid Daney 
145746b903a0SDavid Daney #else
145846b903a0SDavid Daney 
145946b903a0SDavid Daney static int bgx_init_acpi_phy(struct bgx *bgx)
146046b903a0SDavid Daney {
146146b903a0SDavid Daney 	return -ENODEV;
146246b903a0SDavid Daney }
146346b903a0SDavid Daney 
146446b903a0SDavid Daney #endif /* CONFIG_ACPI */
146546b903a0SDavid Daney 
1466de387e11SRobert Richter #if IS_ENABLED(CONFIG_OF_MDIO)
1467de387e11SRobert Richter 
1468de387e11SRobert Richter static int bgx_init_of_phy(struct bgx *bgx)
14694863dea3SSunil Goutham {
1470eee326fdSDavid Daney 	struct fwnode_handle *fwn;
1471b7d3e3d3SDavid Daney 	struct device_node *node = NULL;
14724863dea3SSunil Goutham 	u8 lmac = 0;
14734863dea3SSunil Goutham 
1474eee326fdSDavid Daney 	device_for_each_child_node(&bgx->pdev->dev, fwn) {
14755fc7cf17SDavid Daney 		struct phy_device *pd;
1476eee326fdSDavid Daney 		struct device_node *phy_np;
1477b7d3e3d3SDavid Daney 		const char *mac;
1478de387e11SRobert Richter 
14795fc7cf17SDavid Daney 		/* Should always be an OF node.  But if it is not, we
14805fc7cf17SDavid Daney 		 * cannot handle it, so exit the loop.
1481eee326fdSDavid Daney 		 */
1482b7d3e3d3SDavid Daney 		node = to_of_node(fwn);
1483eee326fdSDavid Daney 		if (!node)
1484eee326fdSDavid Daney 			break;
1485eee326fdSDavid Daney 
1486eee326fdSDavid Daney 		mac = of_get_mac_address(node);
14874863dea3SSunil Goutham 		if (mac)
14884863dea3SSunil Goutham 			ether_addr_copy(bgx->lmac[lmac].mac, mac);
14894863dea3SSunil Goutham 
14904863dea3SSunil Goutham 		SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev);
14914863dea3SSunil Goutham 		bgx->lmac[lmac].lmacid = lmac;
14925fc7cf17SDavid Daney 
14935fc7cf17SDavid Daney 		phy_np = of_parse_phandle(node, "phy-handle", 0);
14945fc7cf17SDavid Daney 		/* If there is no phy or defective firmware presents
14955fc7cf17SDavid Daney 		 * this cortina phy, for which there is no driver
14965fc7cf17SDavid Daney 		 * support, ignore it.
14975fc7cf17SDavid Daney 		 */
14985fc7cf17SDavid Daney 		if (phy_np &&
14995fc7cf17SDavid Daney 		    !of_device_is_compatible(phy_np, "cortina,cs4223-slice")) {
15005fc7cf17SDavid Daney 			/* Wait until the phy drivers are available */
15015fc7cf17SDavid Daney 			pd = of_phy_find_device(phy_np);
15025fc7cf17SDavid Daney 			if (!pd)
1503b7d3e3d3SDavid Daney 				goto defer;
15045fc7cf17SDavid Daney 			bgx->lmac[lmac].phydev = pd;
15055fc7cf17SDavid Daney 		}
15065fc7cf17SDavid Daney 
15074863dea3SSunil Goutham 		lmac++;
15086465859aSSunil Goutham 		if (lmac == bgx->max_lmac) {
150965c66af6SDavid Daney 			of_node_put(node);
15104863dea3SSunil Goutham 			break;
15114863dea3SSunil Goutham 		}
151265c66af6SDavid Daney 	}
1513de387e11SRobert Richter 	return 0;
1514b7d3e3d3SDavid Daney 
1515b7d3e3d3SDavid Daney defer:
1516b7d3e3d3SDavid Daney 	/* We are bailing out, try not to leak device reference counts
1517b7d3e3d3SDavid Daney 	 * for phy devices we may have already found.
1518b7d3e3d3SDavid Daney 	 */
1519b7d3e3d3SDavid Daney 	while (lmac) {
1520b7d3e3d3SDavid Daney 		if (bgx->lmac[lmac].phydev) {
1521b7d3e3d3SDavid Daney 			put_device(&bgx->lmac[lmac].phydev->mdio.dev);
1522b7d3e3d3SDavid Daney 			bgx->lmac[lmac].phydev = NULL;
1523b7d3e3d3SDavid Daney 		}
1524b7d3e3d3SDavid Daney 		lmac--;
1525b7d3e3d3SDavid Daney 	}
1526b7d3e3d3SDavid Daney 	of_node_put(node);
1527b7d3e3d3SDavid Daney 	return -EPROBE_DEFER;
1528de387e11SRobert Richter }
1529de387e11SRobert Richter 
1530de387e11SRobert Richter #else
1531de387e11SRobert Richter 
1532de387e11SRobert Richter static int bgx_init_of_phy(struct bgx *bgx)
1533de387e11SRobert Richter {
1534de387e11SRobert Richter 	return -ENODEV;
1535de387e11SRobert Richter }
1536de387e11SRobert Richter 
1537de387e11SRobert Richter #endif /* CONFIG_OF_MDIO */
1538de387e11SRobert Richter 
1539de387e11SRobert Richter static int bgx_init_phy(struct bgx *bgx)
1540de387e11SRobert Richter {
154146b903a0SDavid Daney 	if (!acpi_disabled)
154246b903a0SDavid Daney 		return bgx_init_acpi_phy(bgx);
154346b903a0SDavid Daney 
1544de387e11SRobert Richter 	return bgx_init_of_phy(bgx);
15454863dea3SSunil Goutham }
15464863dea3SSunil Goutham 
15474863dea3SSunil Goutham static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
15484863dea3SSunil Goutham {
15494863dea3SSunil Goutham 	int err;
15504863dea3SSunil Goutham 	struct device *dev = &pdev->dev;
15514863dea3SSunil Goutham 	struct bgx *bgx = NULL;
15524863dea3SSunil Goutham 	u8 lmac;
155357aaf63cSSunil Goutham 	u16 sdevid;
15544863dea3SSunil Goutham 
15554863dea3SSunil Goutham 	bgx = devm_kzalloc(dev, sizeof(*bgx), GFP_KERNEL);
15564863dea3SSunil Goutham 	if (!bgx)
15574863dea3SSunil Goutham 		return -ENOMEM;
15584863dea3SSunil Goutham 	bgx->pdev = pdev;
15594863dea3SSunil Goutham 
15604863dea3SSunil Goutham 	pci_set_drvdata(pdev, bgx);
15614863dea3SSunil Goutham 
15624863dea3SSunil Goutham 	err = pci_enable_device(pdev);
15634863dea3SSunil Goutham 	if (err) {
15644863dea3SSunil Goutham 		dev_err(dev, "Failed to enable PCI device\n");
15654863dea3SSunil Goutham 		pci_set_drvdata(pdev, NULL);
15664863dea3SSunil Goutham 		return err;
15674863dea3SSunil Goutham 	}
15684863dea3SSunil Goutham 
15694863dea3SSunil Goutham 	err = pci_request_regions(pdev, DRV_NAME);
15704863dea3SSunil Goutham 	if (err) {
15714863dea3SSunil Goutham 		dev_err(dev, "PCI request regions failed 0x%x\n", err);
15724863dea3SSunil Goutham 		goto err_disable_device;
15734863dea3SSunil Goutham 	}
15744863dea3SSunil Goutham 
15754863dea3SSunil Goutham 	/* MAP configuration registers */
15764863dea3SSunil Goutham 	bgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
15774863dea3SSunil Goutham 	if (!bgx->reg_base) {
15784863dea3SSunil Goutham 		dev_err(dev, "BGX: Cannot map CSR memory space, aborting\n");
15794863dea3SSunil Goutham 		err = -ENOMEM;
15804863dea3SSunil Goutham 		goto err_release_regions;
15814863dea3SSunil Goutham 	}
1582d768b678SRobert Richter 
158378aacb6fSSunil Goutham 	set_max_bgx_per_node(pdev);
158478aacb6fSSunil Goutham 
15856465859aSSunil Goutham 	pci_read_config_word(pdev, PCI_DEVICE_ID, &sdevid);
15866465859aSSunil Goutham 	if (sdevid != PCI_DEVICE_ID_THUNDER_RGX) {
1587612e94bdSRadha Mohan Chintakuntla 		bgx->bgx_id = (pci_resource_start(pdev,
1588612e94bdSRadha Mohan Chintakuntla 			PCI_CFG_REG_BAR_NUM) >> 24) & BGX_ID_MASK;
158978aacb6fSSunil Goutham 		bgx->bgx_id += nic_get_node_id(pdev) * max_bgx_per_node;
15906465859aSSunil Goutham 		bgx->max_lmac = MAX_LMAC_PER_BGX;
15914863dea3SSunil Goutham 		bgx_vnic[bgx->bgx_id] = bgx;
15926465859aSSunil Goutham 	} else {
15936465859aSSunil Goutham 		bgx->is_rgx = true;
15946465859aSSunil Goutham 		bgx->max_lmac = 1;
15956465859aSSunil Goutham 		bgx->bgx_id = MAX_BGX_PER_CN81XX - 1;
15966465859aSSunil Goutham 		bgx_vnic[bgx->bgx_id] = bgx;
15976465859aSSunil Goutham 		xcv_init_hw();
15986465859aSSunil Goutham 	}
15996465859aSSunil Goutham 
160009de3917SSunil Goutham 	/* On 81xx all are DLMs and on 83xx there are 3 BGX QLMs and one
160109de3917SSunil Goutham 	 * BGX i.e BGX2 can be split across 2 DLMs.
160209de3917SSunil Goutham 	 */
160309de3917SSunil Goutham 	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid);
160409de3917SSunil Goutham 	if ((sdevid == PCI_SUBSYS_DEVID_81XX_BGX) ||
160509de3917SSunil Goutham 	    ((sdevid == PCI_SUBSYS_DEVID_83XX_BGX) && (bgx->bgx_id == 2)))
160609de3917SSunil Goutham 		bgx->is_dlm = true;
160709de3917SSunil Goutham 
16084863dea3SSunil Goutham 	bgx_get_qlm_mode(bgx);
16094863dea3SSunil Goutham 
1610de387e11SRobert Richter 	err = bgx_init_phy(bgx);
1611de387e11SRobert Richter 	if (err)
1612de387e11SRobert Richter 		goto err_enable;
16134863dea3SSunil Goutham 
16144863dea3SSunil Goutham 	bgx_init_hw(bgx);
16154863dea3SSunil Goutham 
16164863dea3SSunil Goutham 	/* Enable all LMACs */
16174863dea3SSunil Goutham 	for (lmac = 0; lmac < bgx->lmac_count; lmac++) {
16184863dea3SSunil Goutham 		err = bgx_lmac_enable(bgx, lmac);
16194863dea3SSunil Goutham 		if (err) {
16204863dea3SSunil Goutham 			dev_err(dev, "BGX%d failed to enable lmac%d\n",
16214863dea3SSunil Goutham 				bgx->bgx_id, lmac);
162257aaf63cSSunil Goutham 			while (lmac)
162357aaf63cSSunil Goutham 				bgx_lmac_disable(bgx, --lmac);
16244863dea3SSunil Goutham 			goto err_enable;
16254863dea3SSunil Goutham 		}
16264863dea3SSunil Goutham 	}
16274863dea3SSunil Goutham 
16284863dea3SSunil Goutham 	return 0;
16294863dea3SSunil Goutham 
16304863dea3SSunil Goutham err_enable:
16314863dea3SSunil Goutham 	bgx_vnic[bgx->bgx_id] = NULL;
16324863dea3SSunil Goutham err_release_regions:
16334863dea3SSunil Goutham 	pci_release_regions(pdev);
16344863dea3SSunil Goutham err_disable_device:
16354863dea3SSunil Goutham 	pci_disable_device(pdev);
16364863dea3SSunil Goutham 	pci_set_drvdata(pdev, NULL);
16374863dea3SSunil Goutham 	return err;
16384863dea3SSunil Goutham }
16394863dea3SSunil Goutham 
16404863dea3SSunil Goutham static void bgx_remove(struct pci_dev *pdev)
16414863dea3SSunil Goutham {
16424863dea3SSunil Goutham 	struct bgx *bgx = pci_get_drvdata(pdev);
16434863dea3SSunil Goutham 	u8 lmac;
16444863dea3SSunil Goutham 
16454863dea3SSunil Goutham 	/* Disable all LMACs */
16464863dea3SSunil Goutham 	for (lmac = 0; lmac < bgx->lmac_count; lmac++)
16474863dea3SSunil Goutham 		bgx_lmac_disable(bgx, lmac);
16484863dea3SSunil Goutham 
16494863dea3SSunil Goutham 	bgx_vnic[bgx->bgx_id] = NULL;
16504863dea3SSunil Goutham 	pci_release_regions(pdev);
16514863dea3SSunil Goutham 	pci_disable_device(pdev);
16524863dea3SSunil Goutham 	pci_set_drvdata(pdev, NULL);
16534863dea3SSunil Goutham }
16544863dea3SSunil Goutham 
16554863dea3SSunil Goutham static struct pci_driver bgx_driver = {
16564863dea3SSunil Goutham 	.name = DRV_NAME,
16574863dea3SSunil Goutham 	.id_table = bgx_id_table,
16584863dea3SSunil Goutham 	.probe = bgx_probe,
16594863dea3SSunil Goutham 	.remove = bgx_remove,
16604863dea3SSunil Goutham };
16614863dea3SSunil Goutham 
16624863dea3SSunil Goutham static int __init bgx_init_module(void)
16634863dea3SSunil Goutham {
16644863dea3SSunil Goutham 	pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
16654863dea3SSunil Goutham 
16664863dea3SSunil Goutham 	return pci_register_driver(&bgx_driver);
16674863dea3SSunil Goutham }
16684863dea3SSunil Goutham 
16694863dea3SSunil Goutham static void __exit bgx_cleanup_module(void)
16704863dea3SSunil Goutham {
16714863dea3SSunil Goutham 	pci_unregister_driver(&bgx_driver);
16724863dea3SSunil Goutham }
16734863dea3SSunil Goutham 
16744863dea3SSunil Goutham module_init(bgx_init_module);
16754863dea3SSunil Goutham module_exit(bgx_cleanup_module);
1676