18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Cavium, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/acpi.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/phy.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
158c2ecf20Sopenharmony_ci#include <linux/of_net.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "nic_reg.h"
188c2ecf20Sopenharmony_ci#include "nic.h"
198c2ecf20Sopenharmony_ci#include "thunder_bgx.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define DRV_NAME	"thunder_bgx"
228c2ecf20Sopenharmony_ci#define DRV_VERSION	"1.0"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* RX_DMAC_CTL configuration */
258c2ecf20Sopenharmony_cienum MCAST_MODE {
268c2ecf20Sopenharmony_ci		MCAST_MODE_REJECT = 0x0,
278c2ecf20Sopenharmony_ci		MCAST_MODE_ACCEPT = 0x1,
288c2ecf20Sopenharmony_ci		MCAST_MODE_CAM_FILTER = 0x2,
298c2ecf20Sopenharmony_ci		RSVD = 0x3
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define BCAST_ACCEPT      BIT(0)
338c2ecf20Sopenharmony_ci#define CAM_ACCEPT        BIT(3)
348c2ecf20Sopenharmony_ci#define MCAST_MODE_MASK   0x3
358c2ecf20Sopenharmony_ci#define BGX_MCAST_MODE(x) (x << 1)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct dmac_map {
388c2ecf20Sopenharmony_ci	u64                     vf_map;
398c2ecf20Sopenharmony_ci	u64                     dmac;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct lmac {
438c2ecf20Sopenharmony_ci	struct bgx		*bgx;
448c2ecf20Sopenharmony_ci	/* actual number of DMACs configured */
458c2ecf20Sopenharmony_ci	u8			dmacs_cfg;
468c2ecf20Sopenharmony_ci	/* overal number of possible DMACs could be configured per LMAC */
478c2ecf20Sopenharmony_ci	u8                      dmacs_count;
488c2ecf20Sopenharmony_ci	struct dmac_map         *dmacs; /* DMAC:VFs tracking filter array */
498c2ecf20Sopenharmony_ci	u8			mac[ETH_ALEN];
508c2ecf20Sopenharmony_ci	u8                      lmac_type;
518c2ecf20Sopenharmony_ci	u8                      lane_to_sds;
528c2ecf20Sopenharmony_ci	bool                    use_training;
538c2ecf20Sopenharmony_ci	bool                    autoneg;
548c2ecf20Sopenharmony_ci	bool			link_up;
558c2ecf20Sopenharmony_ci	int			lmacid; /* ID within BGX */
568c2ecf20Sopenharmony_ci	int			lmacid_bd; /* ID on board */
578c2ecf20Sopenharmony_ci	struct net_device       netdev;
588c2ecf20Sopenharmony_ci	struct phy_device       *phydev;
598c2ecf20Sopenharmony_ci	unsigned int            last_duplex;
608c2ecf20Sopenharmony_ci	unsigned int            last_link;
618c2ecf20Sopenharmony_ci	unsigned int            last_speed;
628c2ecf20Sopenharmony_ci	bool			is_sgmii;
638c2ecf20Sopenharmony_ci	struct delayed_work	dwork;
648c2ecf20Sopenharmony_ci	struct workqueue_struct *check_link;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct bgx {
688c2ecf20Sopenharmony_ci	u8			bgx_id;
698c2ecf20Sopenharmony_ci	struct	lmac		lmac[MAX_LMAC_PER_BGX];
708c2ecf20Sopenharmony_ci	u8			lmac_count;
718c2ecf20Sopenharmony_ci	u8			max_lmac;
728c2ecf20Sopenharmony_ci	u8                      acpi_lmac_idx;
738c2ecf20Sopenharmony_ci	void __iomem		*reg_base;
748c2ecf20Sopenharmony_ci	struct pci_dev		*pdev;
758c2ecf20Sopenharmony_ci	bool                    is_dlm;
768c2ecf20Sopenharmony_ci	bool                    is_rgx;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct bgx *bgx_vnic[MAX_BGX_THUNDER];
808c2ecf20Sopenharmony_cistatic int lmac_count; /* Total no of LMACs in system */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int bgx_xaui_check_link(struct lmac *lmac);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* Supported devices */
858c2ecf20Sopenharmony_cistatic const struct pci_device_id bgx_id_table[] = {
868c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_BGX) },
878c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_RGX) },
888c2ecf20Sopenharmony_ci	{ 0, }  /* end of table */
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cavium Inc");
928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder BGX/MAC Driver");
938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
948c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bgx_id_table);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* The Cavium ThunderX network controller can *only* be found in SoCs
988c2ecf20Sopenharmony_ci * containing the ThunderX ARM64 CPU implementation.  All accesses to the device
998c2ecf20Sopenharmony_ci * registers on this platform are implicitly strongly ordered with respect
1008c2ecf20Sopenharmony_ci * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use
1018c2ecf20Sopenharmony_ci * with no memory barriers in this driver.  The readq()/writeq() functions add
1028c2ecf20Sopenharmony_ci * explicit ordering operation which in this case are redundant, and only
1038c2ecf20Sopenharmony_ci * add overhead.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Register read/write APIs */
1078c2ecf20Sopenharmony_cistatic u64 bgx_reg_read(struct bgx *bgx, u8 lmac, u64 offset)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return readq_relaxed(addr);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void bgx_reg_write(struct bgx *bgx, u8 lmac, u64 offset, u64 val)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	writeq_relaxed(val, addr);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void bgx_reg_modify(struct bgx *bgx, u8 lmac, u64 offset, u64 val)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	writeq_relaxed(val | readq_relaxed(addr), addr);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int bgx_poll_reg(struct bgx *bgx, u8 lmac, u64 reg, u64 mask, bool zero)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int timeout = 100;
1318c2ecf20Sopenharmony_ci	u64 reg_val;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	while (timeout) {
1348c2ecf20Sopenharmony_ci		reg_val = bgx_reg_read(bgx, lmac, reg);
1358c2ecf20Sopenharmony_ci		if (zero && !(reg_val & mask))
1368c2ecf20Sopenharmony_ci			return 0;
1378c2ecf20Sopenharmony_ci		if (!zero && (reg_val & mask))
1388c2ecf20Sopenharmony_ci			return 0;
1398c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1408c2ecf20Sopenharmony_ci		timeout--;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	return 1;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int max_bgx_per_node;
1468c2ecf20Sopenharmony_cistatic void set_max_bgx_per_node(struct pci_dev *pdev)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	u16 sdevid;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (max_bgx_per_node)
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid);
1548c2ecf20Sopenharmony_ci	switch (sdevid) {
1558c2ecf20Sopenharmony_ci	case PCI_SUBSYS_DEVID_81XX_BGX:
1568c2ecf20Sopenharmony_ci	case PCI_SUBSYS_DEVID_81XX_RGX:
1578c2ecf20Sopenharmony_ci		max_bgx_per_node = MAX_BGX_PER_CN81XX;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case PCI_SUBSYS_DEVID_83XX_BGX:
1608c2ecf20Sopenharmony_ci		max_bgx_per_node = MAX_BGX_PER_CN83XX;
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	case PCI_SUBSYS_DEVID_88XX_BGX:
1638c2ecf20Sopenharmony_ci	default:
1648c2ecf20Sopenharmony_ci		max_bgx_per_node = MAX_BGX_PER_CN88XX;
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct bgx *get_bgx(int node, int bgx_idx)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	int idx = (node * max_bgx_per_node) + bgx_idx;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return bgx_vnic[idx];
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* Return number of BGX present in HW */
1778c2ecf20Sopenharmony_ciunsigned bgx_get_map(int node)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int i;
1808c2ecf20Sopenharmony_ci	unsigned map = 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = 0; i < max_bgx_per_node; i++) {
1838c2ecf20Sopenharmony_ci		if (bgx_vnic[(node * max_bgx_per_node) + i])
1848c2ecf20Sopenharmony_ci			map |= (1 << i);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return map;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_map);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* Return number of LMAC configured for this BGX */
1928c2ecf20Sopenharmony_ciint bgx_get_lmac_count(int node, int bgx_idx)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct bgx *bgx;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	bgx = get_bgx(node, bgx_idx);
1978c2ecf20Sopenharmony_ci	if (bgx)
1988c2ecf20Sopenharmony_ci		return bgx->lmac_count;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_count);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* Returns the current link status of LMAC */
2058c2ecf20Sopenharmony_civoid bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct bgx_link_status *link = (struct bgx_link_status *)status;
2088c2ecf20Sopenharmony_ci	struct bgx *bgx;
2098c2ecf20Sopenharmony_ci	struct lmac *lmac;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	bgx = get_bgx(node, bgx_idx);
2128c2ecf20Sopenharmony_ci	if (!bgx)
2138c2ecf20Sopenharmony_ci		return;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
2168c2ecf20Sopenharmony_ci	link->mac_type = lmac->lmac_type;
2178c2ecf20Sopenharmony_ci	link->link_up = lmac->link_up;
2188c2ecf20Sopenharmony_ci	link->duplex = lmac->last_duplex;
2198c2ecf20Sopenharmony_ci	link->speed = lmac->last_speed;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_link_state);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciconst u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (bgx)
2288c2ecf20Sopenharmony_ci		return bgx->lmac[lmacid].mac;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	return NULL;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_mac);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_civoid bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!bgx)
2398c2ecf20Sopenharmony_ci		return;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ether_addr_copy(bgx->lmac[lmacid].mac, mac);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_set_lmac_mac);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void bgx_flush_dmac_cam_filter(struct bgx *bgx, int lmacid)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct lmac *lmac = NULL;
2488c2ecf20Sopenharmony_ci	u8  idx = 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
2518c2ecf20Sopenharmony_ci	/* reset CAM filters */
2528c2ecf20Sopenharmony_ci	for (idx = 0; idx < lmac->dmacs_count; idx++)
2538c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
2548c2ecf20Sopenharmony_ci			      ((lmacid * lmac->dmacs_count) + idx) *
2558c2ecf20Sopenharmony_ci			      sizeof(u64), 0);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void bgx_lmac_remove_filters(struct lmac *lmac, u8 vf_id)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	int i = 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (!lmac)
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* We've got reset filters request from some of attached VF, while the
2668c2ecf20Sopenharmony_ci	 * others might want to keep their configuration. So in this case lets
2678c2ecf20Sopenharmony_ci	 * iterate over all of configured filters and decrease number of
2688c2ecf20Sopenharmony_ci	 * referencies. if some addresses get zero refs remove them from list
2698c2ecf20Sopenharmony_ci	 */
2708c2ecf20Sopenharmony_ci	for (i = lmac->dmacs_cfg - 1; i >= 0; i--) {
2718c2ecf20Sopenharmony_ci		lmac->dmacs[i].vf_map &= ~BIT_ULL(vf_id);
2728c2ecf20Sopenharmony_ci		if (!lmac->dmacs[i].vf_map) {
2738c2ecf20Sopenharmony_ci			lmac->dmacs_cfg--;
2748c2ecf20Sopenharmony_ci			lmac->dmacs[i].dmac = 0;
2758c2ecf20Sopenharmony_ci			lmac->dmacs[i].vf_map = 0;
2768c2ecf20Sopenharmony_ci		}
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int bgx_lmac_save_filter(struct lmac *lmac, u64 dmac, u8 vf_id)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	u8 i = 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (!lmac)
2858c2ecf20Sopenharmony_ci		return -1;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* At the same time we could have several VFs 'attached' to some
2888c2ecf20Sopenharmony_ci	 * particular LMAC, and each VF is represented as network interface
2898c2ecf20Sopenharmony_ci	 * for kernel. So from user perspective it should be possible to
2908c2ecf20Sopenharmony_ci	 * manipulate with its' (VF) receive modes. However from PF
2918c2ecf20Sopenharmony_ci	 * driver perspective we need to keep track of filter configurations
2928c2ecf20Sopenharmony_ci	 * for different VFs to prevent filter values dupes
2938c2ecf20Sopenharmony_ci	 */
2948c2ecf20Sopenharmony_ci	for (i = 0; i < lmac->dmacs_cfg; i++) {
2958c2ecf20Sopenharmony_ci		if (lmac->dmacs[i].dmac == dmac) {
2968c2ecf20Sopenharmony_ci			lmac->dmacs[i].vf_map |= BIT_ULL(vf_id);
2978c2ecf20Sopenharmony_ci			return -1;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (!(lmac->dmacs_cfg < lmac->dmacs_count))
3028c2ecf20Sopenharmony_ci		return -1;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* keep it for further tracking */
3058c2ecf20Sopenharmony_ci	lmac->dmacs[lmac->dmacs_cfg].dmac = dmac;
3068c2ecf20Sopenharmony_ci	lmac->dmacs[lmac->dmacs_cfg].vf_map = BIT_ULL(vf_id);
3078c2ecf20Sopenharmony_ci	lmac->dmacs_cfg++;
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int bgx_set_dmac_cam_filter_mac(struct bgx *bgx, int lmacid,
3128c2ecf20Sopenharmony_ci				       u64 cam_dmac, u8 idx)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct lmac *lmac = NULL;
3158c2ecf20Sopenharmony_ci	u64 cfg = 0;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* skip zero addresses as meaningless */
3188c2ecf20Sopenharmony_ci	if (!cam_dmac || !bgx)
3198c2ecf20Sopenharmony_ci		return -1;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* configure DCAM filtering for designated LMAC */
3248c2ecf20Sopenharmony_ci	cfg = RX_DMACX_CAM_LMACID(lmacid & LMAC_ID_MASK) |
3258c2ecf20Sopenharmony_ci		RX_DMACX_CAM_EN | cam_dmac;
3268c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM +
3278c2ecf20Sopenharmony_ci		      ((lmacid * lmac->dmacs_count) + idx) * sizeof(u64), cfg);
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_civoid bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid,
3328c2ecf20Sopenharmony_ci			     u64 cam_dmac, u8 vf_id)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
3358c2ecf20Sopenharmony_ci	struct lmac *lmac = NULL;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!bgx)
3388c2ecf20Sopenharmony_ci		return;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (!cam_dmac)
3438c2ecf20Sopenharmony_ci		cam_dmac = ether_addr_to_u64(lmac->mac);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* since we might have several VFs attached to particular LMAC
3468c2ecf20Sopenharmony_ci	 * and kernel could call mcast config for each of them with the
3478c2ecf20Sopenharmony_ci	 * same MAC, check if requested MAC is already in filtering list and
3488c2ecf20Sopenharmony_ci	 * updare/prepare list of MACs to be applied later to HW filters
3498c2ecf20Sopenharmony_ci	 */
3508c2ecf20Sopenharmony_ci	bgx_lmac_save_filter(lmac, cam_dmac, vf_id);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_set_dmac_cam_filter);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_civoid bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
3578c2ecf20Sopenharmony_ci	struct lmac *lmac = NULL;
3588c2ecf20Sopenharmony_ci	u64 cfg = 0;
3598c2ecf20Sopenharmony_ci	u8 i = 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!bgx)
3628c2ecf20Sopenharmony_ci		return;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL);
3678c2ecf20Sopenharmony_ci	if (mode & BGX_XCAST_BCAST_ACCEPT)
3688c2ecf20Sopenharmony_ci		cfg |= BCAST_ACCEPT;
3698c2ecf20Sopenharmony_ci	else
3708c2ecf20Sopenharmony_ci		cfg &= ~BCAST_ACCEPT;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* disable all MCASTs and DMAC filtering */
3738c2ecf20Sopenharmony_ci	cfg &= ~(CAM_ACCEPT | BGX_MCAST_MODE(MCAST_MODE_MASK));
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* check requested bits and set filtergin mode appropriately */
3768c2ecf20Sopenharmony_ci	if (mode & (BGX_XCAST_MCAST_ACCEPT)) {
3778c2ecf20Sopenharmony_ci		cfg |= (BGX_MCAST_MODE(MCAST_MODE_ACCEPT));
3788c2ecf20Sopenharmony_ci	} else if (mode & BGX_XCAST_MCAST_FILTER) {
3798c2ecf20Sopenharmony_ci		cfg |= (BGX_MCAST_MODE(MCAST_MODE_CAM_FILTER) | CAM_ACCEPT);
3808c2ecf20Sopenharmony_ci		for (i = 0; i < lmac->dmacs_cfg; i++)
3818c2ecf20Sopenharmony_ci			bgx_set_dmac_cam_filter_mac(bgx, lmacid,
3828c2ecf20Sopenharmony_ci						    lmac->dmacs[i].dmac, i);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, cfg);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_set_xcast_mode);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_civoid bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf_id)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (!bgx)
3938c2ecf20Sopenharmony_ci		return;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	bgx_lmac_remove_filters(&bgx->lmac[lmacid], vf_id);
3968c2ecf20Sopenharmony_ci	bgx_flush_dmac_cam_filter(bgx, lmacid);
3978c2ecf20Sopenharmony_ci	bgx_set_xcast_mode(node, bgx_idx, lmacid,
3988c2ecf20Sopenharmony_ci			   (BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT));
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_reset_xcast_mode);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_civoid bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
4058c2ecf20Sopenharmony_ci	struct lmac *lmac;
4068c2ecf20Sopenharmony_ci	u64 cfg;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (!bgx)
4098c2ecf20Sopenharmony_ci		return;
4108c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
4138c2ecf20Sopenharmony_ci	if (enable) {
4148c2ecf20Sopenharmony_ci		cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/* enable TX FIFO Underflow interrupt */
4178c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1S,
4188c2ecf20Sopenharmony_ci			       GMI_TXX_INT_UNDFLW);
4198c2ecf20Sopenharmony_ci	} else {
4208c2ecf20Sopenharmony_ci		cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		/* Disable TX FIFO Underflow interrupt */
4238c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1C,
4248c2ecf20Sopenharmony_ci			       GMI_TXX_INT_UNDFLW);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (bgx->is_rgx)
4298c2ecf20Sopenharmony_ci		xcv_setup_link(enable ? lmac->link_up : 0, lmac->last_speed);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/* Enables or disables timestamp insertion by BGX for Rx packets */
4348c2ecf20Sopenharmony_civoid bgx_config_timestamping(int node, int bgx_idx, int lmacid, bool enable)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
4378c2ecf20Sopenharmony_ci	struct lmac *lmac;
4388c2ecf20Sopenharmony_ci	u64 csr_offset, cfg;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (!bgx)
4418c2ecf20Sopenharmony_ci		return;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (lmac->lmac_type == BGX_MODE_SGMII ||
4468c2ecf20Sopenharmony_ci	    lmac->lmac_type == BGX_MODE_QSGMII ||
4478c2ecf20Sopenharmony_ci	    lmac->lmac_type == BGX_MODE_RGMII)
4488c2ecf20Sopenharmony_ci		csr_offset = BGX_GMP_GMI_RXX_FRM_CTL;
4498c2ecf20Sopenharmony_ci	else
4508c2ecf20Sopenharmony_ci		csr_offset = BGX_SMUX_RX_FRM_CTL;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, csr_offset);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (enable)
4558c2ecf20Sopenharmony_ci		cfg |= BGX_PKT_RX_PTP_EN;
4568c2ecf20Sopenharmony_ci	else
4578c2ecf20Sopenharmony_ci		cfg &= ~BGX_PKT_RX_PTP_EN;
4588c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, csr_offset, cfg);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_config_timestamping);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_civoid bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct pfc *pfc = (struct pfc *)pause;
4658c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
4668c2ecf20Sopenharmony_ci	struct lmac *lmac;
4678c2ecf20Sopenharmony_ci	u64 cfg;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (!bgx)
4708c2ecf20Sopenharmony_ci		return;
4718c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
4728c2ecf20Sopenharmony_ci	if (lmac->is_sgmii)
4738c2ecf20Sopenharmony_ci		return;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
4768c2ecf20Sopenharmony_ci	pfc->fc_rx = cfg & RX_EN;
4778c2ecf20Sopenharmony_ci	pfc->fc_tx = cfg & TX_EN;
4788c2ecf20Sopenharmony_ci	pfc->autoneg = 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_get_pfc);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_civoid bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct pfc *pfc = (struct pfc *)pause;
4858c2ecf20Sopenharmony_ci	struct bgx *bgx = get_bgx(node, bgx_idx);
4868c2ecf20Sopenharmony_ci	struct lmac *lmac;
4878c2ecf20Sopenharmony_ci	u64 cfg;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (!bgx)
4908c2ecf20Sopenharmony_ci		return;
4918c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
4928c2ecf20Sopenharmony_ci	if (lmac->is_sgmii)
4938c2ecf20Sopenharmony_ci		return;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL);
4968c2ecf20Sopenharmony_ci	cfg &= ~(RX_EN | TX_EN);
4978c2ecf20Sopenharmony_ci	cfg |= (pfc->fc_rx ? RX_EN : 0x00);
4988c2ecf20Sopenharmony_ci	cfg |= (pfc->fc_tx ? TX_EN : 0x00);
4998c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_set_pfc);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic void bgx_sgmii_change_link_state(struct lmac *lmac)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct bgx *bgx = lmac->bgx;
5068c2ecf20Sopenharmony_ci	u64 cmr_cfg;
5078c2ecf20Sopenharmony_ci	u64 port_cfg = 0;
5088c2ecf20Sopenharmony_ci	u64 misc_ctl = 0;
5098c2ecf20Sopenharmony_ci	bool tx_en, rx_en;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG);
5128c2ecf20Sopenharmony_ci	tx_en = cmr_cfg & CMR_PKT_TX_EN;
5138c2ecf20Sopenharmony_ci	rx_en = cmr_cfg & CMR_PKT_RX_EN;
5148c2ecf20Sopenharmony_ci	cmr_cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
5158c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* Wait for BGX RX to be idle */
5188c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG,
5198c2ecf20Sopenharmony_ci			 GMI_PORT_CFG_RX_IDLE, false)) {
5208c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI RX not idle\n",
5218c2ecf20Sopenharmony_ci			bgx->bgx_id, lmac->lmacid);
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* Wait for BGX TX to be idle */
5268c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG,
5278c2ecf20Sopenharmony_ci			 GMI_PORT_CFG_TX_IDLE, false)) {
5288c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI TX not idle\n",
5298c2ecf20Sopenharmony_ci			bgx->bgx_id, lmac->lmacid);
5308c2ecf20Sopenharmony_ci		return;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG);
5348c2ecf20Sopenharmony_ci	misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (lmac->link_up) {
5378c2ecf20Sopenharmony_ci		misc_ctl &= ~PCS_MISC_CTL_GMX_ENO;
5388c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_DUPLEX;
5398c2ecf20Sopenharmony_ci		port_cfg |=  (lmac->last_duplex << 2);
5408c2ecf20Sopenharmony_ci	} else {
5418c2ecf20Sopenharmony_ci		misc_ctl |= PCS_MISC_CTL_GMX_ENO;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	switch (lmac->last_speed) {
5458c2ecf20Sopenharmony_ci	case 10:
5468c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
5478c2ecf20Sopenharmony_ci		port_cfg |= GMI_PORT_CFG_SPEED_MSB;  /* speed_msb 1 */
5488c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
5498c2ecf20Sopenharmony_ci		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5508c2ecf20Sopenharmony_ci		misc_ctl |= 50; /* samp_pt */
5518c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
5528c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
5538c2ecf20Sopenharmony_ci		break;
5548c2ecf20Sopenharmony_ci	case 100:
5558c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */
5568c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
5578c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */
5588c2ecf20Sopenharmony_ci		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5598c2ecf20Sopenharmony_ci		misc_ctl |= 5; /* samp_pt */
5608c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64);
5618c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0);
5628c2ecf20Sopenharmony_ci		break;
5638c2ecf20Sopenharmony_ci	case 1000:
5648c2ecf20Sopenharmony_ci		port_cfg |= GMI_PORT_CFG_SPEED; /* speed 1 */
5658c2ecf20Sopenharmony_ci		port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */
5668c2ecf20Sopenharmony_ci		port_cfg |= GMI_PORT_CFG_SLOT_TIME; /* slottime 1 */
5678c2ecf20Sopenharmony_ci		misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK;
5688c2ecf20Sopenharmony_ci		misc_ctl |= 1; /* samp_pt */
5698c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 512);
5708c2ecf20Sopenharmony_ci		if (lmac->last_duplex)
5718c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmac->lmacid,
5728c2ecf20Sopenharmony_ci				      BGX_GMP_GMI_TXX_BURST, 0);
5738c2ecf20Sopenharmony_ci		else
5748c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmac->lmacid,
5758c2ecf20Sopenharmony_ci				      BGX_GMP_GMI_TXX_BURST, 8192);
5768c2ecf20Sopenharmony_ci		break;
5778c2ecf20Sopenharmony_ci	default:
5788c2ecf20Sopenharmony_ci		break;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl);
5818c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Restore CMR config settings */
5848c2ecf20Sopenharmony_ci	cmr_cfg |= (rx_en ? CMR_PKT_RX_EN : 0) | (tx_en ? CMR_PKT_TX_EN : 0);
5858c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (bgx->is_rgx && (cmr_cfg & (CMR_PKT_RX_EN | CMR_PKT_TX_EN)))
5888c2ecf20Sopenharmony_ci		xcv_setup_link(lmac->link_up, lmac->last_speed);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void bgx_lmac_handler(struct net_device *netdev)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	struct lmac *lmac = container_of(netdev, struct lmac, netdev);
5948c2ecf20Sopenharmony_ci	struct phy_device *phydev;
5958c2ecf20Sopenharmony_ci	int link_changed = 0;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (!lmac)
5988c2ecf20Sopenharmony_ci		return;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	phydev = lmac->phydev;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	if (!phydev->link && lmac->last_link)
6038c2ecf20Sopenharmony_ci		link_changed = -1;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (phydev->link &&
6068c2ecf20Sopenharmony_ci	    (lmac->last_duplex != phydev->duplex ||
6078c2ecf20Sopenharmony_ci	     lmac->last_link != phydev->link ||
6088c2ecf20Sopenharmony_ci	     lmac->last_speed != phydev->speed)) {
6098c2ecf20Sopenharmony_ci			link_changed = 1;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	lmac->last_link = phydev->link;
6138c2ecf20Sopenharmony_ci	lmac->last_speed = phydev->speed;
6148c2ecf20Sopenharmony_ci	lmac->last_duplex = phydev->duplex;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (!link_changed)
6178c2ecf20Sopenharmony_ci		return;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (link_changed > 0)
6208c2ecf20Sopenharmony_ci		lmac->link_up = true;
6218c2ecf20Sopenharmony_ci	else
6228c2ecf20Sopenharmony_ci		lmac->link_up = false;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (lmac->is_sgmii)
6258c2ecf20Sopenharmony_ci		bgx_sgmii_change_link_state(lmac);
6268c2ecf20Sopenharmony_ci	else
6278c2ecf20Sopenharmony_ci		bgx_xaui_check_link(lmac);
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ciu64 bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct bgx *bgx;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	bgx = get_bgx(node, bgx_idx);
6358c2ecf20Sopenharmony_ci	if (!bgx)
6368c2ecf20Sopenharmony_ci		return 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (idx > 8)
6398c2ecf20Sopenharmony_ci		lmac = 0;
6408c2ecf20Sopenharmony_ci	return bgx_reg_read(bgx, lmac, BGX_CMRX_RX_STAT0 + (idx * 8));
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_rx_stats);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ciu64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct bgx *bgx;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	bgx = get_bgx(node, bgx_idx);
6498c2ecf20Sopenharmony_ci	if (!bgx)
6508c2ecf20Sopenharmony_ci		return 0;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return bgx_reg_read(bgx, lmac, BGX_CMRX_TX_STAT0 + (idx * 8));
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_get_tx_stats);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/* Configure BGX LMAC in internal loopback mode */
6578c2ecf20Sopenharmony_civoid bgx_lmac_internal_loopback(int node, int bgx_idx,
6588c2ecf20Sopenharmony_ci				int lmac_idx, bool enable)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct bgx *bgx;
6618c2ecf20Sopenharmony_ci	struct lmac *lmac;
6628c2ecf20Sopenharmony_ci	u64    cfg;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	bgx = get_bgx(node, bgx_idx);
6658c2ecf20Sopenharmony_ci	if (!bgx)
6668c2ecf20Sopenharmony_ci		return;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmac_idx];
6698c2ecf20Sopenharmony_ci	if (lmac->is_sgmii) {
6708c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL);
6718c2ecf20Sopenharmony_ci		if (enable)
6728c2ecf20Sopenharmony_ci			cfg |= PCS_MRX_CTL_LOOPBACK1;
6738c2ecf20Sopenharmony_ci		else
6748c2ecf20Sopenharmony_ci			cfg &= ~PCS_MRX_CTL_LOOPBACK1;
6758c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg);
6768c2ecf20Sopenharmony_ci	} else {
6778c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1);
6788c2ecf20Sopenharmony_ci		if (enable)
6798c2ecf20Sopenharmony_ci			cfg |= SPU_CTL_LOOPBACK;
6808c2ecf20Sopenharmony_ci		else
6818c2ecf20Sopenharmony_ci			cfg &= ~SPU_CTL_LOOPBACK;
6828c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg);
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_internal_loopback);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	int lmacid = lmac->lmacid;
6908c2ecf20Sopenharmony_ci	u64 cfg;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30);
6938c2ecf20Sopenharmony_ci	/* max packet size */
6948c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	/* Disable frame alignment if using preamble */
6978c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
6988c2ecf20Sopenharmony_ci	if (cfg & 1)
6998c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* Enable lmac */
7028c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	/* PCS reset */
7058c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET);
7068c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL,
7078c2ecf20Sopenharmony_ci			 PCS_MRX_CTL_RESET, true)) {
7088c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX PCS reset not completed\n");
7098c2ecf20Sopenharmony_ci		return -1;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* power down, reset autoneg, autoneg enable */
7138c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL);
7148c2ecf20Sopenharmony_ci	cfg &= ~PCS_MRX_CTL_PWR_DN;
7158c2ecf20Sopenharmony_ci	cfg |= PCS_MRX_CTL_RST_AN;
7168c2ecf20Sopenharmony_ci	if (lmac->phydev) {
7178c2ecf20Sopenharmony_ci		cfg |= PCS_MRX_CTL_AN_EN;
7188c2ecf20Sopenharmony_ci	} else {
7198c2ecf20Sopenharmony_ci		/* In scenarios where PHY driver is not present or it's a
7208c2ecf20Sopenharmony_ci		 * non-standard PHY, FW sets AN_EN to inform Linux driver
7218c2ecf20Sopenharmony_ci		 * to do auto-neg and link polling or not.
7228c2ecf20Sopenharmony_ci		 */
7238c2ecf20Sopenharmony_ci		if (cfg & PCS_MRX_CTL_AN_EN)
7248c2ecf20Sopenharmony_ci			lmac->autoneg = true;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (lmac->lmac_type == BGX_MODE_QSGMII) {
7298c2ecf20Sopenharmony_ci		/* Disable disparity check for QSGMII */
7308c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL);
7318c2ecf20Sopenharmony_ci		cfg &= ~PCS_MISC_CTL_DISP_EN;
7328c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg);
7338c2ecf20Sopenharmony_ci		return 0;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if ((lmac->lmac_type == BGX_MODE_SGMII) && lmac->phydev) {
7378c2ecf20Sopenharmony_ci		if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS,
7388c2ecf20Sopenharmony_ci				 PCS_MRX_STATUS_AN_CPT, false)) {
7398c2ecf20Sopenharmony_ci			dev_err(&bgx->pdev->dev, "BGX AN_CPT not completed\n");
7408c2ecf20Sopenharmony_ci			return -1;
7418c2ecf20Sopenharmony_ci		}
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	return 0;
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistatic int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	u64 cfg;
7508c2ecf20Sopenharmony_ci	int lmacid = lmac->lmacid;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* Reset SPU */
7538c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET);
7548c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
7558c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n");
7568c2ecf20Sopenharmony_ci		return -1;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Disable LMAC */
7608c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
7618c2ecf20Sopenharmony_ci	cfg &= ~CMR_EN;
7628c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
7658c2ecf20Sopenharmony_ci	/* Set interleaved running disparity for RXAUI */
7668c2ecf20Sopenharmony_ci	if (lmac->lmac_type == BGX_MODE_RXAUI)
7678c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL,
7688c2ecf20Sopenharmony_ci			       SPU_MISC_CTL_INTLV_RDISP);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	/* Clear receive packet disable */
7718c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL);
7728c2ecf20Sopenharmony_ci	cfg &= ~SPU_MISC_CTL_RX_DIS;
7738c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* clear all interrupts */
7768c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT);
7778c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg);
7788c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT);
7798c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg);
7808c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
7818c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (lmac->use_training) {
7848c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00);
7858c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00);
7868c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00);
7878c2ecf20Sopenharmony_ci		/* training enable */
7888c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid,
7898c2ecf20Sopenharmony_ci			       BGX_SPUX_BR_PMD_CRTL, SPU_PMD_CRTL_TRAIN_EN);
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	/* Append FCS to each packet */
7938c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/* Disable forward error correction */
7968c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL);
7978c2ecf20Sopenharmony_ci	cfg &= ~SPU_FEC_CTL_FEC_EN;
7988c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/* Disable autoneg */
8018c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL);
8028c2ecf20Sopenharmony_ci	cfg = cfg & ~(SPU_AN_CTL_AN_EN | SPU_AN_CTL_XNP_EN);
8038c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV);
8068c2ecf20Sopenharmony_ci	if (lmac->lmac_type == BGX_MODE_10G_KR)
8078c2ecf20Sopenharmony_ci		cfg |= (1 << 23);
8088c2ecf20Sopenharmony_ci	else if (lmac->lmac_type == BGX_MODE_40G_KR)
8098c2ecf20Sopenharmony_ci		cfg |= (1 << 24);
8108c2ecf20Sopenharmony_ci	else
8118c2ecf20Sopenharmony_ci		cfg &= ~((1 << 23) | (1 << 24));
8128c2ecf20Sopenharmony_ci	cfg = cfg & (~((1ULL << 25) | (1ULL << 22) | (1ULL << 12)));
8138c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL);
8168c2ecf20Sopenharmony_ci	cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN;
8178c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	/* Enable lmac */
8208c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1);
8238c2ecf20Sopenharmony_ci	cfg &= ~SPU_CTL_LOW_POWER;
8248c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL);
8278c2ecf20Sopenharmony_ci	cfg &= ~SMU_TX_CTL_UNI_EN;
8288c2ecf20Sopenharmony_ci	cfg |= SMU_TX_CTL_DIC_EN;
8298c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	/* Enable receive and transmission of pause frames */
8328c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) |
8338c2ecf20Sopenharmony_ci		      BCK_EN | DRP_EN | TX_EN | RX_EN));
8348c2ecf20Sopenharmony_ci	/* Configure pause time and interval */
8358c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid,
8368c2ecf20Sopenharmony_ci		      BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME);
8378c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL);
8388c2ecf20Sopenharmony_ci	cfg &= ~0xFFFFull;
8398c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL,
8408c2ecf20Sopenharmony_ci		      cfg | (DEFAULT_PAUSE_TIME - 0x1000));
8418c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	/* take lmac_count into account */
8448c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1));
8458c2ecf20Sopenharmony_ci	/* max packet size */
8468c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return 0;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic int bgx_xaui_check_link(struct lmac *lmac)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	struct bgx *bgx = lmac->bgx;
8548c2ecf20Sopenharmony_ci	int lmacid = lmac->lmacid;
8558c2ecf20Sopenharmony_ci	int lmac_type = lmac->lmac_type;
8568c2ecf20Sopenharmony_ci	u64 cfg;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (lmac->use_training) {
8598c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
8608c2ecf20Sopenharmony_ci		if (!(cfg & (1ull << 13))) {
8618c2ecf20Sopenharmony_ci			cfg = (1ull << 13) | (1ull << 14);
8628c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
8638c2ecf20Sopenharmony_ci			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL);
8648c2ecf20Sopenharmony_ci			cfg |= (1ull << 0);
8658c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg);
8668c2ecf20Sopenharmony_ci			return -1;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* wait for PCS to come out of reset */
8718c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) {
8728c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n");
8738c2ecf20Sopenharmony_ci		return -1;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if ((lmac_type == BGX_MODE_10G_KR) || (lmac_type == BGX_MODE_XFI) ||
8778c2ecf20Sopenharmony_ci	    (lmac_type == BGX_MODE_40G_KR) || (lmac_type == BGX_MODE_XLAUI)) {
8788c2ecf20Sopenharmony_ci		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1,
8798c2ecf20Sopenharmony_ci				 SPU_BR_STATUS_BLK_LOCK, false)) {
8808c2ecf20Sopenharmony_ci			dev_err(&bgx->pdev->dev,
8818c2ecf20Sopenharmony_ci				"SPU_BR_STATUS_BLK_LOCK not completed\n");
8828c2ecf20Sopenharmony_ci			return -1;
8838c2ecf20Sopenharmony_ci		}
8848c2ecf20Sopenharmony_ci	} else {
8858c2ecf20Sopenharmony_ci		if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS,
8868c2ecf20Sopenharmony_ci				 SPU_BX_STATUS_RX_ALIGN, false)) {
8878c2ecf20Sopenharmony_ci			dev_err(&bgx->pdev->dev,
8888c2ecf20Sopenharmony_ci				"SPU_BX_STATUS_RX_ALIGN not completed\n");
8898c2ecf20Sopenharmony_ci			return -1;
8908c2ecf20Sopenharmony_ci		}
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	/* Clear rcvflt bit (latching high) and read it back */
8948c2ecf20Sopenharmony_ci	if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT)
8958c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid,
8968c2ecf20Sopenharmony_ci			       BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
8978c2ecf20Sopenharmony_ci	if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
8988c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "Receive fault, retry training\n");
8998c2ecf20Sopenharmony_ci		if (lmac->use_training) {
9008c2ecf20Sopenharmony_ci			cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT);
9018c2ecf20Sopenharmony_ci			if (!(cfg & (1ull << 13))) {
9028c2ecf20Sopenharmony_ci				cfg = (1ull << 13) | (1ull << 14);
9038c2ecf20Sopenharmony_ci				bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg);
9048c2ecf20Sopenharmony_ci				cfg = bgx_reg_read(bgx, lmacid,
9058c2ecf20Sopenharmony_ci						   BGX_SPUX_BR_PMD_CRTL);
9068c2ecf20Sopenharmony_ci				cfg |= (1ull << 0);
9078c2ecf20Sopenharmony_ci				bgx_reg_write(bgx, lmacid,
9088c2ecf20Sopenharmony_ci					      BGX_SPUX_BR_PMD_CRTL, cfg);
9098c2ecf20Sopenharmony_ci				return -1;
9108c2ecf20Sopenharmony_ci			}
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci		return -1;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	/* Wait for BGX RX to be idle */
9168c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) {
9178c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "SMU RX not idle\n");
9188c2ecf20Sopenharmony_ci		return -1;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	/* Wait for BGX TX to be idle */
9228c2ecf20Sopenharmony_ci	if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_TX_IDLE, false)) {
9238c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "SMU TX not idle\n");
9248c2ecf20Sopenharmony_ci		return -1;
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* Check for MAC RX faults */
9288c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_CTL);
9298c2ecf20Sopenharmony_ci	/* 0 - Link is okay, 1 - Local fault, 2 - Remote fault */
9308c2ecf20Sopenharmony_ci	cfg &= SMU_RX_CTL_STATUS;
9318c2ecf20Sopenharmony_ci	if (!cfg)
9328c2ecf20Sopenharmony_ci		return 0;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* Rx local/remote fault seen.
9358c2ecf20Sopenharmony_ci	 * Do lmac reinit to see if condition recovers
9368c2ecf20Sopenharmony_ci	 */
9378c2ecf20Sopenharmony_ci	bgx_lmac_xaui_init(bgx, lmac);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	return -1;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic void bgx_poll_for_sgmii_link(struct lmac *lmac)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	u64 pcs_link, an_result;
9458c2ecf20Sopenharmony_ci	u8 speed;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
9488c2ecf20Sopenharmony_ci				BGX_GMP_PCS_MRX_STATUS);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/*Link state bit is sticky, read it again*/
9518c2ecf20Sopenharmony_ci	if (!(pcs_link & PCS_MRX_STATUS_LINK))
9528c2ecf20Sopenharmony_ci		pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid,
9538c2ecf20Sopenharmony_ci					BGX_GMP_PCS_MRX_STATUS);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_GMP_PCS_MRX_STATUS,
9568c2ecf20Sopenharmony_ci			 PCS_MRX_STATUS_AN_CPT, false)) {
9578c2ecf20Sopenharmony_ci		lmac->link_up = false;
9588c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_UNKNOWN;
9598c2ecf20Sopenharmony_ci		lmac->last_duplex = DUPLEX_UNKNOWN;
9608c2ecf20Sopenharmony_ci		goto next_poll;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	lmac->link_up = ((pcs_link & PCS_MRX_STATUS_LINK) != 0) ? true : false;
9648c2ecf20Sopenharmony_ci	an_result = bgx_reg_read(lmac->bgx, lmac->lmacid,
9658c2ecf20Sopenharmony_ci				 BGX_GMP_PCS_ANX_AN_RESULTS);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	speed = (an_result >> 3) & 0x3;
9688c2ecf20Sopenharmony_ci	lmac->last_duplex = (an_result >> 1) & 0x1;
9698c2ecf20Sopenharmony_ci	switch (speed) {
9708c2ecf20Sopenharmony_ci	case 0:
9718c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_10;
9728c2ecf20Sopenharmony_ci		break;
9738c2ecf20Sopenharmony_ci	case 1:
9748c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_100;
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	case 2:
9778c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_1000;
9788c2ecf20Sopenharmony_ci		break;
9798c2ecf20Sopenharmony_ci	default:
9808c2ecf20Sopenharmony_ci		lmac->link_up = false;
9818c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_UNKNOWN;
9828c2ecf20Sopenharmony_ci		lmac->last_duplex = DUPLEX_UNKNOWN;
9838c2ecf20Sopenharmony_ci		break;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cinext_poll:
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	if (lmac->last_link != lmac->link_up) {
9898c2ecf20Sopenharmony_ci		if (lmac->link_up)
9908c2ecf20Sopenharmony_ci			bgx_sgmii_change_link_state(lmac);
9918c2ecf20Sopenharmony_ci		lmac->last_link = lmac->link_up;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 3);
9958c2ecf20Sopenharmony_ci}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic void bgx_poll_for_link(struct work_struct *work)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	struct lmac *lmac;
10008c2ecf20Sopenharmony_ci	u64 spu_link, smu_link;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	lmac = container_of(work, struct lmac, dwork.work);
10038c2ecf20Sopenharmony_ci	if (lmac->is_sgmii) {
10048c2ecf20Sopenharmony_ci		bgx_poll_for_sgmii_link(lmac);
10058c2ecf20Sopenharmony_ci		return;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/* Receive link is latching low. Force it high and verify it */
10098c2ecf20Sopenharmony_ci	bgx_reg_modify(lmac->bgx, lmac->lmacid,
10108c2ecf20Sopenharmony_ci		       BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK);
10118c2ecf20Sopenharmony_ci	bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1,
10128c2ecf20Sopenharmony_ci		     SPU_STATUS1_RCV_LNK, false);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	spu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1);
10158c2ecf20Sopenharmony_ci	smu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if ((spu_link & SPU_STATUS1_RCV_LNK) &&
10188c2ecf20Sopenharmony_ci	    !(smu_link & SMU_RX_CTL_STATUS)) {
10198c2ecf20Sopenharmony_ci		lmac->link_up = true;
10208c2ecf20Sopenharmony_ci		if (lmac->lmac_type == BGX_MODE_XLAUI)
10218c2ecf20Sopenharmony_ci			lmac->last_speed = SPEED_40000;
10228c2ecf20Sopenharmony_ci		else
10238c2ecf20Sopenharmony_ci			lmac->last_speed = SPEED_10000;
10248c2ecf20Sopenharmony_ci		lmac->last_duplex = DUPLEX_FULL;
10258c2ecf20Sopenharmony_ci	} else {
10268c2ecf20Sopenharmony_ci		lmac->link_up = false;
10278c2ecf20Sopenharmony_ci		lmac->last_speed = SPEED_UNKNOWN;
10288c2ecf20Sopenharmony_ci		lmac->last_duplex = DUPLEX_UNKNOWN;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (lmac->last_link != lmac->link_up) {
10328c2ecf20Sopenharmony_ci		if (lmac->link_up) {
10338c2ecf20Sopenharmony_ci			if (bgx_xaui_check_link(lmac)) {
10348c2ecf20Sopenharmony_ci				/* Errors, clear link_up state */
10358c2ecf20Sopenharmony_ci				lmac->link_up = false;
10368c2ecf20Sopenharmony_ci				lmac->last_speed = SPEED_UNKNOWN;
10378c2ecf20Sopenharmony_ci				lmac->last_duplex = DUPLEX_UNKNOWN;
10388c2ecf20Sopenharmony_ci			}
10398c2ecf20Sopenharmony_ci		}
10408c2ecf20Sopenharmony_ci		lmac->last_link = lmac->link_up;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 2);
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_cistatic int phy_interface_mode(u8 lmac_type)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	if (lmac_type == BGX_MODE_QSGMII)
10498c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_QSGMII;
10508c2ecf20Sopenharmony_ci	if (lmac_type == BGX_MODE_RGMII)
10518c2ecf20Sopenharmony_ci		return PHY_INTERFACE_MODE_RGMII_RXID;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return PHY_INTERFACE_MODE_SGMII;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	struct lmac *lmac;
10598c2ecf20Sopenharmony_ci	u64 cfg;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
10628c2ecf20Sopenharmony_ci	lmac->bgx = bgx;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	if ((lmac->lmac_type == BGX_MODE_SGMII) ||
10658c2ecf20Sopenharmony_ci	    (lmac->lmac_type == BGX_MODE_QSGMII) ||
10668c2ecf20Sopenharmony_ci	    (lmac->lmac_type == BGX_MODE_RGMII)) {
10678c2ecf20Sopenharmony_ci		lmac->is_sgmii = true;
10688c2ecf20Sopenharmony_ci		if (bgx_lmac_sgmii_init(bgx, lmac))
10698c2ecf20Sopenharmony_ci			return -1;
10708c2ecf20Sopenharmony_ci	} else {
10718c2ecf20Sopenharmony_ci		lmac->is_sgmii = false;
10728c2ecf20Sopenharmony_ci		if (bgx_lmac_xaui_init(bgx, lmac))
10738c2ecf20Sopenharmony_ci			return -1;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (lmac->is_sgmii) {
10778c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND);
10788c2ecf20Sopenharmony_ci		cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
10798c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg);
10808c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1);
10818c2ecf20Sopenharmony_ci	} else {
10828c2ecf20Sopenharmony_ci		cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND);
10838c2ecf20Sopenharmony_ci		cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */
10848c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg);
10858c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4);
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	/* actual number of filters available to exact LMAC */
10898c2ecf20Sopenharmony_ci	lmac->dmacs_count = (RX_DMAC_COUNT / bgx->lmac_count);
10908c2ecf20Sopenharmony_ci	lmac->dmacs = kcalloc(lmac->dmacs_count, sizeof(*lmac->dmacs),
10918c2ecf20Sopenharmony_ci			      GFP_KERNEL);
10928c2ecf20Sopenharmony_ci	if (!lmac->dmacs)
10938c2ecf20Sopenharmony_ci		return -ENOMEM;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* Enable lmac */
10968c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	/* Restore default cfg, incase low level firmware changed it */
10998c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	if ((lmac->lmac_type != BGX_MODE_XFI) &&
11028c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_XLAUI) &&
11038c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_40G_KR) &&
11048c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_10G_KR)) {
11058c2ecf20Sopenharmony_ci		if (!lmac->phydev) {
11068c2ecf20Sopenharmony_ci			if (lmac->autoneg) {
11078c2ecf20Sopenharmony_ci				bgx_reg_write(bgx, lmacid,
11088c2ecf20Sopenharmony_ci					      BGX_GMP_PCS_LINKX_TIMER,
11098c2ecf20Sopenharmony_ci					      PCS_LINKX_TIMER_COUNT);
11108c2ecf20Sopenharmony_ci				goto poll;
11118c2ecf20Sopenharmony_ci			} else {
11128c2ecf20Sopenharmony_ci				/* Default to below link speed and duplex */
11138c2ecf20Sopenharmony_ci				lmac->link_up = true;
11148c2ecf20Sopenharmony_ci				lmac->last_speed = SPEED_1000;
11158c2ecf20Sopenharmony_ci				lmac->last_duplex = DUPLEX_FULL;
11168c2ecf20Sopenharmony_ci				bgx_sgmii_change_link_state(lmac);
11178c2ecf20Sopenharmony_ci				return 0;
11188c2ecf20Sopenharmony_ci			}
11198c2ecf20Sopenharmony_ci		}
11208c2ecf20Sopenharmony_ci		lmac->phydev->dev_flags = 0;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		if (phy_connect_direct(&lmac->netdev, lmac->phydev,
11238c2ecf20Sopenharmony_ci				       bgx_lmac_handler,
11248c2ecf20Sopenharmony_ci				       phy_interface_mode(lmac->lmac_type)))
11258c2ecf20Sopenharmony_ci			return -ENODEV;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		phy_start(lmac->phydev);
11288c2ecf20Sopenharmony_ci		return 0;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cipoll:
11328c2ecf20Sopenharmony_ci	lmac->check_link = alloc_workqueue("check_link", WQ_UNBOUND |
11338c2ecf20Sopenharmony_ci					   WQ_MEM_RECLAIM, 1);
11348c2ecf20Sopenharmony_ci	if (!lmac->check_link)
11358c2ecf20Sopenharmony_ci		return -ENOMEM;
11368c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&lmac->dwork, bgx_poll_for_link);
11378c2ecf20Sopenharmony_ci	queue_delayed_work(lmac->check_link, &lmac->dwork, 0);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	return 0;
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct lmac *lmac;
11458c2ecf20Sopenharmony_ci	u64 cfg;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
11488c2ecf20Sopenharmony_ci	if (lmac->check_link) {
11498c2ecf20Sopenharmony_ci		/* Destroy work queue */
11508c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&lmac->dwork);
11518c2ecf20Sopenharmony_ci		destroy_workqueue(lmac->check_link);
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* Disable packet reception */
11558c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11568c2ecf20Sopenharmony_ci	cfg &= ~CMR_PKT_RX_EN;
11578c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	/* Give chance for Rx/Tx FIFO to get drained */
11608c2ecf20Sopenharmony_ci	bgx_poll_reg(bgx, lmacid, BGX_CMRX_RX_FIFO_LEN, (u64)0x1FFF, true);
11618c2ecf20Sopenharmony_ci	bgx_poll_reg(bgx, lmacid, BGX_CMRX_TX_FIFO_LEN, (u64)0x3FFF, true);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* Disable packet transmission */
11648c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11658c2ecf20Sopenharmony_ci	cfg &= ~CMR_PKT_TX_EN;
11668c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* Disable serdes lanes */
11698c2ecf20Sopenharmony_ci        if (!lmac->is_sgmii)
11708c2ecf20Sopenharmony_ci                bgx_reg_modify(bgx, lmacid,
11718c2ecf20Sopenharmony_ci                               BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
11728c2ecf20Sopenharmony_ci        else
11738c2ecf20Sopenharmony_ci                bgx_reg_modify(bgx, lmacid,
11748c2ecf20Sopenharmony_ci                               BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_PWR_DN);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/* Disable LMAC */
11778c2ecf20Sopenharmony_ci	cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
11788c2ecf20Sopenharmony_ci	cfg &= ~CMR_EN;
11798c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	bgx_flush_dmac_cam_filter(bgx, lmacid);
11828c2ecf20Sopenharmony_ci	kfree(lmac->dmacs);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if ((lmac->lmac_type != BGX_MODE_XFI) &&
11858c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_XLAUI) &&
11868c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_40G_KR) &&
11878c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_10G_KR) && lmac->phydev)
11888c2ecf20Sopenharmony_ci		phy_disconnect(lmac->phydev);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	lmac->phydev = NULL;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic void bgx_init_hw(struct bgx *bgx)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	int i;
11968c2ecf20Sopenharmony_ci	struct lmac *lmac;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP);
11998c2ecf20Sopenharmony_ci	if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS))
12008c2ecf20Sopenharmony_ci		dev_err(&bgx->pdev->dev, "BGX%d BIST failed\n", bgx->bgx_id);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	/* Set lmac type and lane2serdes mapping */
12038c2ecf20Sopenharmony_ci	for (i = 0; i < bgx->lmac_count; i++) {
12048c2ecf20Sopenharmony_ci		lmac = &bgx->lmac[i];
12058c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, i, BGX_CMRX_CFG,
12068c2ecf20Sopenharmony_ci			      (lmac->lmac_type << 8) | lmac->lane_to_sds);
12078c2ecf20Sopenharmony_ci		bgx->lmac[i].lmacid_bd = lmac_count;
12088c2ecf20Sopenharmony_ci		lmac_count++;
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, bgx->lmac_count);
12128c2ecf20Sopenharmony_ci	bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, bgx->lmac_count);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* Set the backpressure AND mask */
12158c2ecf20Sopenharmony_ci	for (i = 0; i < bgx->lmac_count; i++)
12168c2ecf20Sopenharmony_ci		bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND,
12178c2ecf20Sopenharmony_ci			       ((1ULL << MAX_BGX_CHANS_PER_LMAC) - 1) <<
12188c2ecf20Sopenharmony_ci			       (i * MAX_BGX_CHANS_PER_LMAC));
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	/* Disable all MAC filtering */
12218c2ecf20Sopenharmony_ci	for (i = 0; i < RX_DMAC_COUNT; i++)
12228c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	/* Disable MAC steering (NCSI traffic) */
12258c2ecf20Sopenharmony_ci	for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++)
12268c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, 0, BGX_CMR_RX_STEERING + (i * 8), 0x00);
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic u8 bgx_get_lane2sds_cfg(struct bgx *bgx, struct lmac *lmac)
12308c2ecf20Sopenharmony_ci{
12318c2ecf20Sopenharmony_ci	return (u8)(bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG) & 0xFF);
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic void bgx_print_qlm_mode(struct bgx *bgx, u8 lmacid)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	struct device *dev = &bgx->pdev->dev;
12378c2ecf20Sopenharmony_ci	struct lmac *lmac;
12388c2ecf20Sopenharmony_ci	char str[27];
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	if (!bgx->is_dlm && lmacid)
12418c2ecf20Sopenharmony_ci		return;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[lmacid];
12448c2ecf20Sopenharmony_ci	if (!bgx->is_dlm)
12458c2ecf20Sopenharmony_ci		sprintf(str, "BGX%d QLM mode", bgx->bgx_id);
12468c2ecf20Sopenharmony_ci	else
12478c2ecf20Sopenharmony_ci		sprintf(str, "BGX%d LMAC%d mode", bgx->bgx_id, lmacid);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	switch (lmac->lmac_type) {
12508c2ecf20Sopenharmony_ci	case BGX_MODE_SGMII:
12518c2ecf20Sopenharmony_ci		dev_info(dev, "%s: SGMII\n", (char *)str);
12528c2ecf20Sopenharmony_ci		break;
12538c2ecf20Sopenharmony_ci	case BGX_MODE_XAUI:
12548c2ecf20Sopenharmony_ci		dev_info(dev, "%s: XAUI\n", (char *)str);
12558c2ecf20Sopenharmony_ci		break;
12568c2ecf20Sopenharmony_ci	case BGX_MODE_RXAUI:
12578c2ecf20Sopenharmony_ci		dev_info(dev, "%s: RXAUI\n", (char *)str);
12588c2ecf20Sopenharmony_ci		break;
12598c2ecf20Sopenharmony_ci	case BGX_MODE_XFI:
12608c2ecf20Sopenharmony_ci		if (!lmac->use_training)
12618c2ecf20Sopenharmony_ci			dev_info(dev, "%s: XFI\n", (char *)str);
12628c2ecf20Sopenharmony_ci		else
12638c2ecf20Sopenharmony_ci			dev_info(dev, "%s: 10G_KR\n", (char *)str);
12648c2ecf20Sopenharmony_ci		break;
12658c2ecf20Sopenharmony_ci	case BGX_MODE_XLAUI:
12668c2ecf20Sopenharmony_ci		if (!lmac->use_training)
12678c2ecf20Sopenharmony_ci			dev_info(dev, "%s: XLAUI\n", (char *)str);
12688c2ecf20Sopenharmony_ci		else
12698c2ecf20Sopenharmony_ci			dev_info(dev, "%s: 40G_KR4\n", (char *)str);
12708c2ecf20Sopenharmony_ci		break;
12718c2ecf20Sopenharmony_ci	case BGX_MODE_QSGMII:
12728c2ecf20Sopenharmony_ci		dev_info(dev, "%s: QSGMII\n", (char *)str);
12738c2ecf20Sopenharmony_ci		break;
12748c2ecf20Sopenharmony_ci	case BGX_MODE_RGMII:
12758c2ecf20Sopenharmony_ci		dev_info(dev, "%s: RGMII\n", (char *)str);
12768c2ecf20Sopenharmony_ci		break;
12778c2ecf20Sopenharmony_ci	case BGX_MODE_INVALID:
12788c2ecf20Sopenharmony_ci		/* Nothing to do */
12798c2ecf20Sopenharmony_ci		break;
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic void lmac_set_lane2sds(struct bgx *bgx, struct lmac *lmac)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	switch (lmac->lmac_type) {
12868c2ecf20Sopenharmony_ci	case BGX_MODE_SGMII:
12878c2ecf20Sopenharmony_ci	case BGX_MODE_XFI:
12888c2ecf20Sopenharmony_ci		lmac->lane_to_sds = lmac->lmacid;
12898c2ecf20Sopenharmony_ci		break;
12908c2ecf20Sopenharmony_ci	case BGX_MODE_XAUI:
12918c2ecf20Sopenharmony_ci	case BGX_MODE_XLAUI:
12928c2ecf20Sopenharmony_ci	case BGX_MODE_RGMII:
12938c2ecf20Sopenharmony_ci		lmac->lane_to_sds = 0xE4;
12948c2ecf20Sopenharmony_ci		break;
12958c2ecf20Sopenharmony_ci	case BGX_MODE_RXAUI:
12968c2ecf20Sopenharmony_ci		lmac->lane_to_sds = (lmac->lmacid) ? 0xE : 0x4;
12978c2ecf20Sopenharmony_ci		break;
12988c2ecf20Sopenharmony_ci	case BGX_MODE_QSGMII:
12998c2ecf20Sopenharmony_ci		/* There is no way to determine if DLM0/2 is QSGMII or
13008c2ecf20Sopenharmony_ci		 * DLM1/3 is configured to QSGMII as bootloader will
13018c2ecf20Sopenharmony_ci		 * configure all LMACs, so take whatever is configured
13028c2ecf20Sopenharmony_ci		 * by low level firmware.
13038c2ecf20Sopenharmony_ci		 */
13048c2ecf20Sopenharmony_ci		lmac->lane_to_sds = bgx_get_lane2sds_cfg(bgx, lmac);
13058c2ecf20Sopenharmony_ci		break;
13068c2ecf20Sopenharmony_ci	default:
13078c2ecf20Sopenharmony_ci		lmac->lane_to_sds = 0;
13088c2ecf20Sopenharmony_ci		break;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_cistatic void lmac_set_training(struct bgx *bgx, struct lmac *lmac, int lmacid)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	if ((lmac->lmac_type != BGX_MODE_10G_KR) &&
13158c2ecf20Sopenharmony_ci	    (lmac->lmac_type != BGX_MODE_40G_KR)) {
13168c2ecf20Sopenharmony_ci		lmac->use_training = false;
13178c2ecf20Sopenharmony_ci		return;
13188c2ecf20Sopenharmony_ci	}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	lmac->use_training = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL) &
13218c2ecf20Sopenharmony_ci							SPU_PMD_CRTL_TRAIN_EN;
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic void bgx_set_lmac_config(struct bgx *bgx, u8 idx)
13258c2ecf20Sopenharmony_ci{
13268c2ecf20Sopenharmony_ci	struct lmac *lmac;
13278c2ecf20Sopenharmony_ci	u64 cmr_cfg;
13288c2ecf20Sopenharmony_ci	u8 lmac_type;
13298c2ecf20Sopenharmony_ci	u8 lane_to_sds;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	lmac = &bgx->lmac[idx];
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (!bgx->is_dlm || bgx->is_rgx) {
13348c2ecf20Sopenharmony_ci		/* Read LMAC0 type to figure out QLM mode
13358c2ecf20Sopenharmony_ci		 * This is configured by low level firmware
13368c2ecf20Sopenharmony_ci		 */
13378c2ecf20Sopenharmony_ci		cmr_cfg = bgx_reg_read(bgx, 0, BGX_CMRX_CFG);
13388c2ecf20Sopenharmony_ci		lmac->lmac_type = (cmr_cfg >> 8) & 0x07;
13398c2ecf20Sopenharmony_ci		if (bgx->is_rgx)
13408c2ecf20Sopenharmony_ci			lmac->lmac_type = BGX_MODE_RGMII;
13418c2ecf20Sopenharmony_ci		lmac_set_training(bgx, lmac, 0);
13428c2ecf20Sopenharmony_ci		lmac_set_lane2sds(bgx, lmac);
13438c2ecf20Sopenharmony_ci		return;
13448c2ecf20Sopenharmony_ci	}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	/* For DLMs or SLMs on 80/81/83xx so many lane configurations
13478c2ecf20Sopenharmony_ci	 * are possible and vary across boards. Also Kernel doesn't have
13488c2ecf20Sopenharmony_ci	 * any way to identify board type/info and since firmware does,
13498c2ecf20Sopenharmony_ci	 * just take lmac type and serdes lane config as is.
13508c2ecf20Sopenharmony_ci	 */
13518c2ecf20Sopenharmony_ci	cmr_cfg = bgx_reg_read(bgx, idx, BGX_CMRX_CFG);
13528c2ecf20Sopenharmony_ci	lmac_type = (u8)((cmr_cfg >> 8) & 0x07);
13538c2ecf20Sopenharmony_ci	lane_to_sds = (u8)(cmr_cfg & 0xFF);
13548c2ecf20Sopenharmony_ci	/* Check if config is reset value */
13558c2ecf20Sopenharmony_ci	if ((lmac_type == 0) && (lane_to_sds == 0xE4))
13568c2ecf20Sopenharmony_ci		lmac->lmac_type = BGX_MODE_INVALID;
13578c2ecf20Sopenharmony_ci	else
13588c2ecf20Sopenharmony_ci		lmac->lmac_type = lmac_type;
13598c2ecf20Sopenharmony_ci	lmac->lane_to_sds = lane_to_sds;
13608c2ecf20Sopenharmony_ci	lmac_set_training(bgx, lmac, lmac->lmacid);
13618c2ecf20Sopenharmony_ci}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_cistatic void bgx_get_qlm_mode(struct bgx *bgx)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	struct lmac *lmac;
13668c2ecf20Sopenharmony_ci	u8  idx;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	/* Init all LMAC's type to invalid */
13698c2ecf20Sopenharmony_ci	for (idx = 0; idx < bgx->max_lmac; idx++) {
13708c2ecf20Sopenharmony_ci		lmac = &bgx->lmac[idx];
13718c2ecf20Sopenharmony_ci		lmac->lmacid = idx;
13728c2ecf20Sopenharmony_ci		lmac->lmac_type = BGX_MODE_INVALID;
13738c2ecf20Sopenharmony_ci		lmac->use_training = false;
13748c2ecf20Sopenharmony_ci	}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	/* It is assumed that low level firmware sets this value */
13778c2ecf20Sopenharmony_ci	bgx->lmac_count = bgx_reg_read(bgx, 0, BGX_CMR_RX_LMACS) & 0x7;
13788c2ecf20Sopenharmony_ci	if (bgx->lmac_count > bgx->max_lmac)
13798c2ecf20Sopenharmony_ci		bgx->lmac_count = bgx->max_lmac;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	for (idx = 0; idx < bgx->lmac_count; idx++) {
13828c2ecf20Sopenharmony_ci		bgx_set_lmac_config(bgx, idx);
13838c2ecf20Sopenharmony_ci		bgx_print_qlm_mode(bgx, idx);
13848c2ecf20Sopenharmony_ci	}
13858c2ecf20Sopenharmony_ci}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int acpi_get_mac_address(struct device *dev, struct acpi_device *adev,
13908c2ecf20Sopenharmony_ci				u8 *dst)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	u8 mac[ETH_ALEN];
13938c2ecf20Sopenharmony_ci	u8 *addr;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	addr = fwnode_get_mac_address(acpi_fwnode_handle(adev), mac, ETH_ALEN);
13968c2ecf20Sopenharmony_ci	if (!addr) {
13978c2ecf20Sopenharmony_ci		dev_err(dev, "MAC address invalid: %pM\n", mac);
13988c2ecf20Sopenharmony_ci		return -EINVAL;
13998c2ecf20Sopenharmony_ci	}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	dev_info(dev, "MAC address set to: %pM\n", mac);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	ether_addr_copy(dst, mac);
14048c2ecf20Sopenharmony_ci	return 0;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci/* Currently only sets the MAC address. */
14088c2ecf20Sopenharmony_cistatic acpi_status bgx_acpi_register_phy(acpi_handle handle,
14098c2ecf20Sopenharmony_ci					 u32 lvl, void *context, void **rv)
14108c2ecf20Sopenharmony_ci{
14118c2ecf20Sopenharmony_ci	struct bgx *bgx = context;
14128c2ecf20Sopenharmony_ci	struct device *dev = &bgx->pdev->dev;
14138c2ecf20Sopenharmony_ci	struct acpi_device *adev;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (acpi_bus_get_device(handle, &adev))
14168c2ecf20Sopenharmony_ci		goto out;
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	acpi_get_mac_address(dev, adev, bgx->lmac[bgx->acpi_lmac_idx].mac);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(&bgx->lmac[bgx->acpi_lmac_idx].netdev, dev);
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	bgx->lmac[bgx->acpi_lmac_idx].lmacid = bgx->acpi_lmac_idx;
14238c2ecf20Sopenharmony_ci	bgx->acpi_lmac_idx++; /* move to next LMAC */
14248c2ecf20Sopenharmony_ciout:
14258c2ecf20Sopenharmony_ci	return AE_OK;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_cistatic acpi_status bgx_acpi_match_id(acpi_handle handle, u32 lvl,
14298c2ecf20Sopenharmony_ci				     void *context, void **ret_val)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
14328c2ecf20Sopenharmony_ci	struct bgx *bgx = context;
14338c2ecf20Sopenharmony_ci	char bgx_sel[5];
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	snprintf(bgx_sel, 5, "BGX%d", bgx->bgx_id);
14368c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &string))) {
14378c2ecf20Sopenharmony_ci		pr_warn("Invalid link device\n");
14388c2ecf20Sopenharmony_ci		return AE_OK;
14398c2ecf20Sopenharmony_ci	}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (strncmp(string.pointer, bgx_sel, 4)) {
14428c2ecf20Sopenharmony_ci		kfree(string.pointer);
14438c2ecf20Sopenharmony_ci		return AE_OK;
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
14478c2ecf20Sopenharmony_ci			    bgx_acpi_register_phy, NULL, bgx, NULL);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	kfree(string.pointer);
14508c2ecf20Sopenharmony_ci	return AE_CTRL_TERMINATE;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic int bgx_init_acpi_phy(struct bgx *bgx)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	acpi_get_devices(NULL, bgx_acpi_match_id, bgx, (void **)NULL);
14568c2ecf20Sopenharmony_ci	return 0;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci#else
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic int bgx_init_acpi_phy(struct bgx *bgx)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	return -ENODEV;
14648c2ecf20Sopenharmony_ci}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci#endif /* CONFIG_ACPI */
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_MDIO)
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic int bgx_init_of_phy(struct bgx *bgx)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	struct fwnode_handle *fwn;
14738c2ecf20Sopenharmony_ci	struct device_node *node = NULL;
14748c2ecf20Sopenharmony_ci	u8 lmac = 0;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	device_for_each_child_node(&bgx->pdev->dev, fwn) {
14778c2ecf20Sopenharmony_ci		struct phy_device *pd;
14788c2ecf20Sopenharmony_ci		struct device_node *phy_np;
14798c2ecf20Sopenharmony_ci		const char *mac;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci		/* Should always be an OF node.  But if it is not, we
14828c2ecf20Sopenharmony_ci		 * cannot handle it, so exit the loop.
14838c2ecf20Sopenharmony_ci		 */
14848c2ecf20Sopenharmony_ci		node = to_of_node(fwn);
14858c2ecf20Sopenharmony_ci		if (!node)
14868c2ecf20Sopenharmony_ci			break;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci		mac = of_get_mac_address(node);
14898c2ecf20Sopenharmony_ci		if (!IS_ERR(mac))
14908c2ecf20Sopenharmony_ci			ether_addr_copy(bgx->lmac[lmac].mac, mac);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci		SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev);
14938c2ecf20Sopenharmony_ci		bgx->lmac[lmac].lmacid = lmac;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci		phy_np = of_parse_phandle(node, "phy-handle", 0);
14968c2ecf20Sopenharmony_ci		/* If there is no phy or defective firmware presents
14978c2ecf20Sopenharmony_ci		 * this cortina phy, for which there is no driver
14988c2ecf20Sopenharmony_ci		 * support, ignore it.
14998c2ecf20Sopenharmony_ci		 */
15008c2ecf20Sopenharmony_ci		if (phy_np &&
15018c2ecf20Sopenharmony_ci		    !of_device_is_compatible(phy_np, "cortina,cs4223-slice")) {
15028c2ecf20Sopenharmony_ci			/* Wait until the phy drivers are available */
15038c2ecf20Sopenharmony_ci			pd = of_phy_find_device(phy_np);
15048c2ecf20Sopenharmony_ci			if (!pd)
15058c2ecf20Sopenharmony_ci				goto defer;
15068c2ecf20Sopenharmony_ci			bgx->lmac[lmac].phydev = pd;
15078c2ecf20Sopenharmony_ci		}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci		lmac++;
15108c2ecf20Sopenharmony_ci		if (lmac == bgx->max_lmac) {
15118c2ecf20Sopenharmony_ci			of_node_put(node);
15128c2ecf20Sopenharmony_ci			break;
15138c2ecf20Sopenharmony_ci		}
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci	return 0;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cidefer:
15188c2ecf20Sopenharmony_ci	/* We are bailing out, try not to leak device reference counts
15198c2ecf20Sopenharmony_ci	 * for phy devices we may have already found.
15208c2ecf20Sopenharmony_ci	 */
15218c2ecf20Sopenharmony_ci	while (lmac) {
15228c2ecf20Sopenharmony_ci		if (bgx->lmac[lmac].phydev) {
15238c2ecf20Sopenharmony_ci			put_device(&bgx->lmac[lmac].phydev->mdio.dev);
15248c2ecf20Sopenharmony_ci			bgx->lmac[lmac].phydev = NULL;
15258c2ecf20Sopenharmony_ci		}
15268c2ecf20Sopenharmony_ci		lmac--;
15278c2ecf20Sopenharmony_ci	}
15288c2ecf20Sopenharmony_ci	of_node_put(node);
15298c2ecf20Sopenharmony_ci	return -EPROBE_DEFER;
15308c2ecf20Sopenharmony_ci}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci#else
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cistatic int bgx_init_of_phy(struct bgx *bgx)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	return -ENODEV;
15378c2ecf20Sopenharmony_ci}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci#endif /* CONFIG_OF_MDIO */
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_cistatic int bgx_init_phy(struct bgx *bgx)
15428c2ecf20Sopenharmony_ci{
15438c2ecf20Sopenharmony_ci	if (!acpi_disabled)
15448c2ecf20Sopenharmony_ci		return bgx_init_acpi_phy(bgx);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	return bgx_init_of_phy(bgx);
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic irqreturn_t bgx_intr_handler(int irq, void *data)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	struct bgx *bgx = (struct bgx *)data;
15528c2ecf20Sopenharmony_ci	u64 status, val;
15538c2ecf20Sopenharmony_ci	int lmac;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	for (lmac = 0; lmac < bgx->lmac_count; lmac++) {
15568c2ecf20Sopenharmony_ci		status = bgx_reg_read(bgx, lmac, BGX_GMP_GMI_TXX_INT);
15578c2ecf20Sopenharmony_ci		if (status & GMI_TXX_INT_UNDFLW) {
15588c2ecf20Sopenharmony_ci			pci_err(bgx->pdev, "BGX%d lmac%d UNDFLW\n",
15598c2ecf20Sopenharmony_ci				bgx->bgx_id, lmac);
15608c2ecf20Sopenharmony_ci			val = bgx_reg_read(bgx, lmac, BGX_CMRX_CFG);
15618c2ecf20Sopenharmony_ci			val &= ~CMR_EN;
15628c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val);
15638c2ecf20Sopenharmony_ci			val |= CMR_EN;
15648c2ecf20Sopenharmony_ci			bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val);
15658c2ecf20Sopenharmony_ci		}
15668c2ecf20Sopenharmony_ci		/* clear interrupts */
15678c2ecf20Sopenharmony_ci		bgx_reg_write(bgx, lmac, BGX_GMP_GMI_TXX_INT, status);
15688c2ecf20Sopenharmony_ci	}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic void bgx_register_intr(struct pci_dev *pdev)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	struct bgx *bgx = pci_get_drvdata(pdev);
15768c2ecf20Sopenharmony_ci	int ret;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, BGX_LMAC_VEC_OFFSET,
15798c2ecf20Sopenharmony_ci				    BGX_LMAC_VEC_OFFSET, PCI_IRQ_ALL_TYPES);
15808c2ecf20Sopenharmony_ci	if (ret < 0) {
15818c2ecf20Sopenharmony_ci		pci_err(pdev, "Req for #%d msix vectors failed\n",
15828c2ecf20Sopenharmony_ci			BGX_LMAC_VEC_OFFSET);
15838c2ecf20Sopenharmony_ci		return;
15848c2ecf20Sopenharmony_ci	}
15858c2ecf20Sopenharmony_ci	ret = pci_request_irq(pdev, GMPX_GMI_TX_INT, bgx_intr_handler, NULL,
15868c2ecf20Sopenharmony_ci			      bgx, "BGX%d", bgx->bgx_id);
15878c2ecf20Sopenharmony_ci	if (ret)
15888c2ecf20Sopenharmony_ci		pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx);
15898c2ecf20Sopenharmony_ci}
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cistatic int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
15928c2ecf20Sopenharmony_ci{
15938c2ecf20Sopenharmony_ci	int err;
15948c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
15958c2ecf20Sopenharmony_ci	struct bgx *bgx = NULL;
15968c2ecf20Sopenharmony_ci	u8 lmac;
15978c2ecf20Sopenharmony_ci	u16 sdevid;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	bgx = devm_kzalloc(dev, sizeof(*bgx), GFP_KERNEL);
16008c2ecf20Sopenharmony_ci	if (!bgx)
16018c2ecf20Sopenharmony_ci		return -ENOMEM;
16028c2ecf20Sopenharmony_ci	bgx->pdev = pdev;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, bgx);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	err = pcim_enable_device(pdev);
16078c2ecf20Sopenharmony_ci	if (err) {
16088c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable PCI device\n");
16098c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
16108c2ecf20Sopenharmony_ci		return err;
16118c2ecf20Sopenharmony_ci	}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
16148c2ecf20Sopenharmony_ci	if (err) {
16158c2ecf20Sopenharmony_ci		dev_err(dev, "PCI request regions failed 0x%x\n", err);
16168c2ecf20Sopenharmony_ci		goto err_disable_device;
16178c2ecf20Sopenharmony_ci	}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* MAP configuration registers */
16208c2ecf20Sopenharmony_ci	bgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
16218c2ecf20Sopenharmony_ci	if (!bgx->reg_base) {
16228c2ecf20Sopenharmony_ci		dev_err(dev, "BGX: Cannot map CSR memory space, aborting\n");
16238c2ecf20Sopenharmony_ci		err = -ENOMEM;
16248c2ecf20Sopenharmony_ci		goto err_release_regions;
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	set_max_bgx_per_node(pdev);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, PCI_DEVICE_ID, &sdevid);
16308c2ecf20Sopenharmony_ci	if (sdevid != PCI_DEVICE_ID_THUNDER_RGX) {
16318c2ecf20Sopenharmony_ci		bgx->bgx_id = (pci_resource_start(pdev,
16328c2ecf20Sopenharmony_ci			PCI_CFG_REG_BAR_NUM) >> 24) & BGX_ID_MASK;
16338c2ecf20Sopenharmony_ci		bgx->bgx_id += nic_get_node_id(pdev) * max_bgx_per_node;
16348c2ecf20Sopenharmony_ci		bgx->max_lmac = MAX_LMAC_PER_BGX;
16358c2ecf20Sopenharmony_ci		bgx_vnic[bgx->bgx_id] = bgx;
16368c2ecf20Sopenharmony_ci	} else {
16378c2ecf20Sopenharmony_ci		bgx->is_rgx = true;
16388c2ecf20Sopenharmony_ci		bgx->max_lmac = 1;
16398c2ecf20Sopenharmony_ci		bgx->bgx_id = MAX_BGX_PER_CN81XX - 1;
16408c2ecf20Sopenharmony_ci		bgx_vnic[bgx->bgx_id] = bgx;
16418c2ecf20Sopenharmony_ci		xcv_init_hw();
16428c2ecf20Sopenharmony_ci	}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	/* On 81xx all are DLMs and on 83xx there are 3 BGX QLMs and one
16458c2ecf20Sopenharmony_ci	 * BGX i.e BGX2 can be split across 2 DLMs.
16468c2ecf20Sopenharmony_ci	 */
16478c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid);
16488c2ecf20Sopenharmony_ci	if ((sdevid == PCI_SUBSYS_DEVID_81XX_BGX) ||
16498c2ecf20Sopenharmony_ci	    ((sdevid == PCI_SUBSYS_DEVID_83XX_BGX) && (bgx->bgx_id == 2)))
16508c2ecf20Sopenharmony_ci		bgx->is_dlm = true;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	bgx_get_qlm_mode(bgx);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	err = bgx_init_phy(bgx);
16558c2ecf20Sopenharmony_ci	if (err)
16568c2ecf20Sopenharmony_ci		goto err_enable;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	bgx_init_hw(bgx);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	bgx_register_intr(pdev);
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	/* Enable all LMACs */
16638c2ecf20Sopenharmony_ci	for (lmac = 0; lmac < bgx->lmac_count; lmac++) {
16648c2ecf20Sopenharmony_ci		err = bgx_lmac_enable(bgx, lmac);
16658c2ecf20Sopenharmony_ci		if (err) {
16668c2ecf20Sopenharmony_ci			dev_err(dev, "BGX%d failed to enable lmac%d\n",
16678c2ecf20Sopenharmony_ci				bgx->bgx_id, lmac);
16688c2ecf20Sopenharmony_ci			while (lmac)
16698c2ecf20Sopenharmony_ci				bgx_lmac_disable(bgx, --lmac);
16708c2ecf20Sopenharmony_ci			goto err_enable;
16718c2ecf20Sopenharmony_ci		}
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	return 0;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_cierr_enable:
16778c2ecf20Sopenharmony_ci	bgx_vnic[bgx->bgx_id] = NULL;
16788c2ecf20Sopenharmony_ci	pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx);
16798c2ecf20Sopenharmony_cierr_release_regions:
16808c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
16818c2ecf20Sopenharmony_cierr_disable_device:
16828c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
16838c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
16848c2ecf20Sopenharmony_ci	return err;
16858c2ecf20Sopenharmony_ci}
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_cistatic void bgx_remove(struct pci_dev *pdev)
16888c2ecf20Sopenharmony_ci{
16898c2ecf20Sopenharmony_ci	struct bgx *bgx = pci_get_drvdata(pdev);
16908c2ecf20Sopenharmony_ci	u8 lmac;
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	/* Disable all LMACs */
16938c2ecf20Sopenharmony_ci	for (lmac = 0; lmac < bgx->lmac_count; lmac++)
16948c2ecf20Sopenharmony_ci		bgx_lmac_disable(bgx, lmac);
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	bgx_vnic[bgx->bgx_id] = NULL;
16998c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
17008c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
17018c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_cistatic struct pci_driver bgx_driver = {
17058c2ecf20Sopenharmony_ci	.name = DRV_NAME,
17068c2ecf20Sopenharmony_ci	.id_table = bgx_id_table,
17078c2ecf20Sopenharmony_ci	.probe = bgx_probe,
17088c2ecf20Sopenharmony_ci	.remove = bgx_remove,
17098c2ecf20Sopenharmony_ci};
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic int __init bgx_init_module(void)
17128c2ecf20Sopenharmony_ci{
17138c2ecf20Sopenharmony_ci	pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	return pci_register_driver(&bgx_driver);
17168c2ecf20Sopenharmony_ci}
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_cistatic void __exit bgx_cleanup_module(void)
17198c2ecf20Sopenharmony_ci{
17208c2ecf20Sopenharmony_ci	pci_unregister_driver(&bgx_driver);
17218c2ecf20Sopenharmony_ci}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cimodule_init(bgx_init_module);
17248c2ecf20Sopenharmony_cimodule_exit(bgx_cleanup_module);
1725