162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Cavium, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/acpi.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/netdevice.h> 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_mdio.h> 1562306a36Sopenharmony_ci#include <linux/of_net.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "nic_reg.h" 1862306a36Sopenharmony_ci#include "nic.h" 1962306a36Sopenharmony_ci#include "thunder_bgx.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define DRV_NAME "thunder_bgx" 2262306a36Sopenharmony_ci#define DRV_VERSION "1.0" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* RX_DMAC_CTL configuration */ 2562306a36Sopenharmony_cienum MCAST_MODE { 2662306a36Sopenharmony_ci MCAST_MODE_REJECT = 0x0, 2762306a36Sopenharmony_ci MCAST_MODE_ACCEPT = 0x1, 2862306a36Sopenharmony_ci MCAST_MODE_CAM_FILTER = 0x2, 2962306a36Sopenharmony_ci RSVD = 0x3 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define BCAST_ACCEPT BIT(0) 3362306a36Sopenharmony_ci#define CAM_ACCEPT BIT(3) 3462306a36Sopenharmony_ci#define MCAST_MODE_MASK 0x3 3562306a36Sopenharmony_ci#define BGX_MCAST_MODE(x) (x << 1) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct dmac_map { 3862306a36Sopenharmony_ci u64 vf_map; 3962306a36Sopenharmony_ci u64 dmac; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct lmac { 4362306a36Sopenharmony_ci struct bgx *bgx; 4462306a36Sopenharmony_ci /* actual number of DMACs configured */ 4562306a36Sopenharmony_ci u8 dmacs_cfg; 4662306a36Sopenharmony_ci /* overal number of possible DMACs could be configured per LMAC */ 4762306a36Sopenharmony_ci u8 dmacs_count; 4862306a36Sopenharmony_ci struct dmac_map *dmacs; /* DMAC:VFs tracking filter array */ 4962306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 5062306a36Sopenharmony_ci u8 lmac_type; 5162306a36Sopenharmony_ci u8 lane_to_sds; 5262306a36Sopenharmony_ci bool use_training; 5362306a36Sopenharmony_ci bool autoneg; 5462306a36Sopenharmony_ci bool link_up; 5562306a36Sopenharmony_ci int lmacid; /* ID within BGX */ 5662306a36Sopenharmony_ci int lmacid_bd; /* ID on board */ 5762306a36Sopenharmony_ci struct net_device netdev; 5862306a36Sopenharmony_ci struct phy_device *phydev; 5962306a36Sopenharmony_ci unsigned int last_duplex; 6062306a36Sopenharmony_ci unsigned int last_link; 6162306a36Sopenharmony_ci unsigned int last_speed; 6262306a36Sopenharmony_ci bool is_sgmii; 6362306a36Sopenharmony_ci struct delayed_work dwork; 6462306a36Sopenharmony_ci struct workqueue_struct *check_link; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct bgx { 6862306a36Sopenharmony_ci u8 bgx_id; 6962306a36Sopenharmony_ci struct lmac lmac[MAX_LMAC_PER_BGX]; 7062306a36Sopenharmony_ci u8 lmac_count; 7162306a36Sopenharmony_ci u8 max_lmac; 7262306a36Sopenharmony_ci u8 acpi_lmac_idx; 7362306a36Sopenharmony_ci void __iomem *reg_base; 7462306a36Sopenharmony_ci struct pci_dev *pdev; 7562306a36Sopenharmony_ci bool is_dlm; 7662306a36Sopenharmony_ci bool is_rgx; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct bgx *bgx_vnic[MAX_BGX_THUNDER]; 8062306a36Sopenharmony_cistatic int lmac_count; /* Total no of LMACs in system */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int bgx_xaui_check_link(struct lmac *lmac); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Supported devices */ 8562306a36Sopenharmony_cistatic const struct pci_device_id bgx_id_table[] = { 8662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_BGX) }, 8762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_RGX) }, 8862306a36Sopenharmony_ci { 0, } /* end of table */ 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciMODULE_AUTHOR("Cavium Inc"); 9262306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder BGX/MAC Driver"); 9362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 9462306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 9562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bgx_id_table); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* The Cavium ThunderX network controller can *only* be found in SoCs 9862306a36Sopenharmony_ci * containing the ThunderX ARM64 CPU implementation. All accesses to the device 9962306a36Sopenharmony_ci * registers on this platform are implicitly strongly ordered with respect 10062306a36Sopenharmony_ci * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use 10162306a36Sopenharmony_ci * with no memory barriers in this driver. The readq()/writeq() functions add 10262306a36Sopenharmony_ci * explicit ordering operation which in this case are redundant, and only 10362306a36Sopenharmony_ci * add overhead. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Register read/write APIs */ 10762306a36Sopenharmony_cistatic u64 bgx_reg_read(struct bgx *bgx, u8 lmac, u64 offset) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return readq_relaxed(addr); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void bgx_reg_write(struct bgx *bgx, u8 lmac, u64 offset, u64 val) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci writeq_relaxed(val, addr); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void bgx_reg_modify(struct bgx *bgx, u8 lmac, u64 offset, u64 val) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci void __iomem *addr = bgx->reg_base + ((u32)lmac << 20) + offset; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci writeq_relaxed(val | readq_relaxed(addr), addr); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int bgx_poll_reg(struct bgx *bgx, u8 lmac, u64 reg, u64 mask, bool zero) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int timeout = 100; 13162306a36Sopenharmony_ci u64 reg_val; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci while (timeout) { 13462306a36Sopenharmony_ci reg_val = bgx_reg_read(bgx, lmac, reg); 13562306a36Sopenharmony_ci if (zero && !(reg_val & mask)) 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci if (!zero && (reg_val & mask)) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci usleep_range(1000, 2000); 14062306a36Sopenharmony_ci timeout--; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci return 1; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int max_bgx_per_node; 14662306a36Sopenharmony_cistatic void set_max_bgx_per_node(struct pci_dev *pdev) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci u16 sdevid; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (max_bgx_per_node) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid); 15462306a36Sopenharmony_ci switch (sdevid) { 15562306a36Sopenharmony_ci case PCI_SUBSYS_DEVID_81XX_BGX: 15662306a36Sopenharmony_ci case PCI_SUBSYS_DEVID_81XX_RGX: 15762306a36Sopenharmony_ci max_bgx_per_node = MAX_BGX_PER_CN81XX; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case PCI_SUBSYS_DEVID_83XX_BGX: 16062306a36Sopenharmony_ci max_bgx_per_node = MAX_BGX_PER_CN83XX; 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case PCI_SUBSYS_DEVID_88XX_BGX: 16362306a36Sopenharmony_ci default: 16462306a36Sopenharmony_ci max_bgx_per_node = MAX_BGX_PER_CN88XX; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct bgx *get_bgx(int node, int bgx_idx) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int idx = (node * max_bgx_per_node) + bgx_idx; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return bgx_vnic[idx]; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* Return number of BGX present in HW */ 17762306a36Sopenharmony_ciunsigned bgx_get_map(int node) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci unsigned map = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < max_bgx_per_node; i++) { 18362306a36Sopenharmony_ci if (bgx_vnic[(node * max_bgx_per_node) + i]) 18462306a36Sopenharmony_ci map |= (1 << i); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return map; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_map); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* Return number of LMAC configured for this BGX */ 19262306a36Sopenharmony_ciint bgx_get_lmac_count(int node, int bgx_idx) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct bgx *bgx; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci bgx = get_bgx(node, bgx_idx); 19762306a36Sopenharmony_ci if (bgx) 19862306a36Sopenharmony_ci return bgx->lmac_count; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_count); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* Returns the current link status of LMAC */ 20562306a36Sopenharmony_civoid bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct bgx_link_status *link = (struct bgx_link_status *)status; 20862306a36Sopenharmony_ci struct bgx *bgx; 20962306a36Sopenharmony_ci struct lmac *lmac; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci bgx = get_bgx(node, bgx_idx); 21262306a36Sopenharmony_ci if (!bgx) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 21662306a36Sopenharmony_ci link->mac_type = lmac->lmac_type; 21762306a36Sopenharmony_ci link->link_up = lmac->link_up; 21862306a36Sopenharmony_ci link->duplex = lmac->last_duplex; 21962306a36Sopenharmony_ci link->speed = lmac->last_speed; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_link_state); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciconst u8 *bgx_get_lmac_mac(int node, int bgx_idx, int lmacid) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (bgx) 22862306a36Sopenharmony_ci return bgx->lmac[lmacid].mac; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return NULL; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_lmac_mac); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!bgx) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ether_addr_copy(bgx->lmac[lmacid].mac, mac); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_set_lmac_mac); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void bgx_flush_dmac_cam_filter(struct bgx *bgx, int lmacid) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct lmac *lmac = NULL; 24862306a36Sopenharmony_ci u8 idx = 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 25162306a36Sopenharmony_ci /* reset CAM filters */ 25262306a36Sopenharmony_ci for (idx = 0; idx < lmac->dmacs_count; idx++) 25362306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + 25462306a36Sopenharmony_ci ((lmacid * lmac->dmacs_count) + idx) * 25562306a36Sopenharmony_ci sizeof(u64), 0); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void bgx_lmac_remove_filters(struct lmac *lmac, u8 vf_id) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int i = 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!lmac) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* We've got reset filters request from some of attached VF, while the 26662306a36Sopenharmony_ci * others might want to keep their configuration. So in this case lets 26762306a36Sopenharmony_ci * iterate over all of configured filters and decrease number of 26862306a36Sopenharmony_ci * referencies. if some addresses get zero refs remove them from list 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci for (i = lmac->dmacs_cfg - 1; i >= 0; i--) { 27162306a36Sopenharmony_ci lmac->dmacs[i].vf_map &= ~BIT_ULL(vf_id); 27262306a36Sopenharmony_ci if (!lmac->dmacs[i].vf_map) { 27362306a36Sopenharmony_ci lmac->dmacs_cfg--; 27462306a36Sopenharmony_ci lmac->dmacs[i].dmac = 0; 27562306a36Sopenharmony_ci lmac->dmacs[i].vf_map = 0; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int bgx_lmac_save_filter(struct lmac *lmac, u64 dmac, u8 vf_id) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci u8 i = 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!lmac) 28562306a36Sopenharmony_ci return -1; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* At the same time we could have several VFs 'attached' to some 28862306a36Sopenharmony_ci * particular LMAC, and each VF is represented as network interface 28962306a36Sopenharmony_ci * for kernel. So from user perspective it should be possible to 29062306a36Sopenharmony_ci * manipulate with its' (VF) receive modes. However from PF 29162306a36Sopenharmony_ci * driver perspective we need to keep track of filter configurations 29262306a36Sopenharmony_ci * for different VFs to prevent filter values dupes 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci for (i = 0; i < lmac->dmacs_cfg; i++) { 29562306a36Sopenharmony_ci if (lmac->dmacs[i].dmac == dmac) { 29662306a36Sopenharmony_ci lmac->dmacs[i].vf_map |= BIT_ULL(vf_id); 29762306a36Sopenharmony_ci return -1; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (!(lmac->dmacs_cfg < lmac->dmacs_count)) 30262306a36Sopenharmony_ci return -1; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* keep it for further tracking */ 30562306a36Sopenharmony_ci lmac->dmacs[lmac->dmacs_cfg].dmac = dmac; 30662306a36Sopenharmony_ci lmac->dmacs[lmac->dmacs_cfg].vf_map = BIT_ULL(vf_id); 30762306a36Sopenharmony_ci lmac->dmacs_cfg++; 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int bgx_set_dmac_cam_filter_mac(struct bgx *bgx, int lmacid, 31262306a36Sopenharmony_ci u64 cam_dmac, u8 idx) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct lmac *lmac = NULL; 31562306a36Sopenharmony_ci u64 cfg = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* skip zero addresses as meaningless */ 31862306a36Sopenharmony_ci if (!cam_dmac || !bgx) 31962306a36Sopenharmony_ci return -1; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* configure DCAM filtering for designated LMAC */ 32462306a36Sopenharmony_ci cfg = RX_DMACX_CAM_LMACID(lmacid & LMAC_ID_MASK) | 32562306a36Sopenharmony_ci RX_DMACX_CAM_EN | cam_dmac; 32662306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + 32762306a36Sopenharmony_ci ((lmacid * lmac->dmacs_count) + idx) * sizeof(u64), cfg); 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_civoid bgx_set_dmac_cam_filter(int node, int bgx_idx, int lmacid, 33262306a36Sopenharmony_ci u64 cam_dmac, u8 vf_id) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 33562306a36Sopenharmony_ci struct lmac *lmac = NULL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!bgx) 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!cam_dmac) 34362306a36Sopenharmony_ci cam_dmac = ether_addr_to_u64(lmac->mac); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* since we might have several VFs attached to particular LMAC 34662306a36Sopenharmony_ci * and kernel could call mcast config for each of them with the 34762306a36Sopenharmony_ci * same MAC, check if requested MAC is already in filtering list and 34862306a36Sopenharmony_ci * updare/prepare list of MACs to be applied later to HW filters 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci bgx_lmac_save_filter(lmac, cam_dmac, vf_id); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_set_dmac_cam_filter); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_civoid bgx_set_xcast_mode(int node, int bgx_idx, int lmacid, u8 mode) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 35762306a36Sopenharmony_ci struct lmac *lmac = NULL; 35862306a36Sopenharmony_ci u64 cfg = 0; 35962306a36Sopenharmony_ci u8 i = 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!bgx) 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL); 36762306a36Sopenharmony_ci if (mode & BGX_XCAST_BCAST_ACCEPT) 36862306a36Sopenharmony_ci cfg |= BCAST_ACCEPT; 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci cfg &= ~BCAST_ACCEPT; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* disable all MCASTs and DMAC filtering */ 37362306a36Sopenharmony_ci cfg &= ~(CAM_ACCEPT | BGX_MCAST_MODE(MCAST_MODE_MASK)); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* check requested bits and set filtergin mode appropriately */ 37662306a36Sopenharmony_ci if (mode & (BGX_XCAST_MCAST_ACCEPT)) { 37762306a36Sopenharmony_ci cfg |= (BGX_MCAST_MODE(MCAST_MODE_ACCEPT)); 37862306a36Sopenharmony_ci } else if (mode & BGX_XCAST_MCAST_FILTER) { 37962306a36Sopenharmony_ci cfg |= (BGX_MCAST_MODE(MCAST_MODE_CAM_FILTER) | CAM_ACCEPT); 38062306a36Sopenharmony_ci for (i = 0; i < lmac->dmacs_cfg; i++) 38162306a36Sopenharmony_ci bgx_set_dmac_cam_filter_mac(bgx, lmacid, 38262306a36Sopenharmony_ci lmac->dmacs[i].dmac, i); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, cfg); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_set_xcast_mode); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_civoid bgx_reset_xcast_mode(int node, int bgx_idx, int lmacid, u8 vf_id) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!bgx) 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci bgx_lmac_remove_filters(&bgx->lmac[lmacid], vf_id); 39662306a36Sopenharmony_ci bgx_flush_dmac_cam_filter(bgx, lmacid); 39762306a36Sopenharmony_ci bgx_set_xcast_mode(node, bgx_idx, lmacid, 39862306a36Sopenharmony_ci (BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT)); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_reset_xcast_mode); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 40562306a36Sopenharmony_ci struct lmac *lmac; 40662306a36Sopenharmony_ci u64 cfg; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!bgx) 40962306a36Sopenharmony_ci return; 41062306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); 41362306a36Sopenharmony_ci if (enable) { 41462306a36Sopenharmony_ci cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* enable TX FIFO Underflow interrupt */ 41762306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1S, 41862306a36Sopenharmony_ci GMI_TXX_INT_UNDFLW); 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Disable TX FIFO Underflow interrupt */ 42362306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_INT_ENA_W1C, 42462306a36Sopenharmony_ci GMI_TXX_INT_UNDFLW); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (bgx->is_rgx) 42962306a36Sopenharmony_ci xcv_setup_link(enable ? lmac->link_up : 0, lmac->last_speed); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_rx_tx_enable); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* Enables or disables timestamp insertion by BGX for Rx packets */ 43462306a36Sopenharmony_civoid bgx_config_timestamping(int node, int bgx_idx, int lmacid, bool enable) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 43762306a36Sopenharmony_ci struct lmac *lmac; 43862306a36Sopenharmony_ci u64 csr_offset, cfg; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!bgx) 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (lmac->lmac_type == BGX_MODE_SGMII || 44662306a36Sopenharmony_ci lmac->lmac_type == BGX_MODE_QSGMII || 44762306a36Sopenharmony_ci lmac->lmac_type == BGX_MODE_RGMII) 44862306a36Sopenharmony_ci csr_offset = BGX_GMP_GMI_RXX_FRM_CTL; 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci csr_offset = BGX_SMUX_RX_FRM_CTL; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, csr_offset); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (enable) 45562306a36Sopenharmony_ci cfg |= BGX_PKT_RX_PTP_EN; 45662306a36Sopenharmony_ci else 45762306a36Sopenharmony_ci cfg &= ~BGX_PKT_RX_PTP_EN; 45862306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, csr_offset, cfg); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_config_timestamping); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_civoid bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct pfc *pfc = (struct pfc *)pause; 46562306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 46662306a36Sopenharmony_ci struct lmac *lmac; 46762306a36Sopenharmony_ci u64 cfg; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!bgx) 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 47262306a36Sopenharmony_ci if (lmac->is_sgmii) 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL); 47662306a36Sopenharmony_ci pfc->fc_rx = cfg & RX_EN; 47762306a36Sopenharmony_ci pfc->fc_tx = cfg & TX_EN; 47862306a36Sopenharmony_ci pfc->autoneg = 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_get_pfc); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_civoid bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct pfc *pfc = (struct pfc *)pause; 48562306a36Sopenharmony_ci struct bgx *bgx = get_bgx(node, bgx_idx); 48662306a36Sopenharmony_ci struct lmac *lmac; 48762306a36Sopenharmony_ci u64 cfg; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!bgx) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 49262306a36Sopenharmony_ci if (lmac->is_sgmii) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_CBFC_CTL); 49662306a36Sopenharmony_ci cfg &= ~(RX_EN | TX_EN); 49762306a36Sopenharmony_ci cfg |= (pfc->fc_rx ? RX_EN : 0x00); 49862306a36Sopenharmony_ci cfg |= (pfc->fc_tx ? TX_EN : 0x00); 49962306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, cfg); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_set_pfc); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void bgx_sgmii_change_link_state(struct lmac *lmac) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct bgx *bgx = lmac->bgx; 50662306a36Sopenharmony_ci u64 cmr_cfg; 50762306a36Sopenharmony_ci u64 port_cfg = 0; 50862306a36Sopenharmony_ci u64 misc_ctl = 0; 50962306a36Sopenharmony_ci bool tx_en, rx_en; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG); 51262306a36Sopenharmony_ci tx_en = cmr_cfg & CMR_PKT_TX_EN; 51362306a36Sopenharmony_ci rx_en = cmr_cfg & CMR_PKT_RX_EN; 51462306a36Sopenharmony_ci cmr_cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); 51562306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Wait for BGX RX to be idle */ 51862306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, 51962306a36Sopenharmony_ci GMI_PORT_CFG_RX_IDLE, false)) { 52062306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI RX not idle\n", 52162306a36Sopenharmony_ci bgx->bgx_id, lmac->lmacid); 52262306a36Sopenharmony_ci return; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Wait for BGX TX to be idle */ 52662306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, 52762306a36Sopenharmony_ci GMI_PORT_CFG_TX_IDLE, false)) { 52862306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI TX not idle\n", 52962306a36Sopenharmony_ci bgx->bgx_id, lmac->lmacid); 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG); 53462306a36Sopenharmony_ci misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (lmac->link_up) { 53762306a36Sopenharmony_ci misc_ctl &= ~PCS_MISC_CTL_GMX_ENO; 53862306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_DUPLEX; 53962306a36Sopenharmony_ci port_cfg |= (lmac->last_duplex << 2); 54062306a36Sopenharmony_ci } else { 54162306a36Sopenharmony_ci misc_ctl |= PCS_MISC_CTL_GMX_ENO; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci switch (lmac->last_speed) { 54562306a36Sopenharmony_ci case 10: 54662306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */ 54762306a36Sopenharmony_ci port_cfg |= GMI_PORT_CFG_SPEED_MSB; /* speed_msb 1 */ 54862306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */ 54962306a36Sopenharmony_ci misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; 55062306a36Sopenharmony_ci misc_ctl |= 50; /* samp_pt */ 55162306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64); 55262306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0); 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci case 100: 55562306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SPEED; /* speed 0 */ 55662306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */ 55762306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SLOT_TIME; /* slottime 0 */ 55862306a36Sopenharmony_ci misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; 55962306a36Sopenharmony_ci misc_ctl |= 5; /* samp_pt */ 56062306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 64); 56162306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_BURST, 0); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci case 1000: 56462306a36Sopenharmony_ci port_cfg |= GMI_PORT_CFG_SPEED; /* speed 1 */ 56562306a36Sopenharmony_ci port_cfg &= ~GMI_PORT_CFG_SPEED_MSB; /* speed_msb 0 */ 56662306a36Sopenharmony_ci port_cfg |= GMI_PORT_CFG_SLOT_TIME; /* slottime 1 */ 56762306a36Sopenharmony_ci misc_ctl &= ~PCS_MISC_CTL_SAMP_PT_MASK; 56862306a36Sopenharmony_ci misc_ctl |= 1; /* samp_pt */ 56962306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_TXX_SLOT, 512); 57062306a36Sopenharmony_ci if (lmac->last_duplex) 57162306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, 57262306a36Sopenharmony_ci BGX_GMP_GMI_TXX_BURST, 0); 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, 57562306a36Sopenharmony_ci BGX_GMP_GMI_TXX_BURST, 8192); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci default: 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl); 58162306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Restore CMR config settings */ 58462306a36Sopenharmony_ci cmr_cfg |= (rx_en ? CMR_PKT_RX_EN : 0) | (tx_en ? CMR_PKT_TX_EN : 0); 58562306a36Sopenharmony_ci bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (bgx->is_rgx && (cmr_cfg & (CMR_PKT_RX_EN | CMR_PKT_TX_EN))) 58862306a36Sopenharmony_ci xcv_setup_link(lmac->link_up, lmac->last_speed); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void bgx_lmac_handler(struct net_device *netdev) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct lmac *lmac = container_of(netdev, struct lmac, netdev); 59462306a36Sopenharmony_ci struct phy_device *phydev; 59562306a36Sopenharmony_ci int link_changed = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci phydev = lmac->phydev; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!phydev->link && lmac->last_link) 60062306a36Sopenharmony_ci link_changed = -1; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (phydev->link && 60362306a36Sopenharmony_ci (lmac->last_duplex != phydev->duplex || 60462306a36Sopenharmony_ci lmac->last_link != phydev->link || 60562306a36Sopenharmony_ci lmac->last_speed != phydev->speed)) { 60662306a36Sopenharmony_ci link_changed = 1; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci lmac->last_link = phydev->link; 61062306a36Sopenharmony_ci lmac->last_speed = phydev->speed; 61162306a36Sopenharmony_ci lmac->last_duplex = phydev->duplex; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!link_changed) 61462306a36Sopenharmony_ci return; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (link_changed > 0) 61762306a36Sopenharmony_ci lmac->link_up = true; 61862306a36Sopenharmony_ci else 61962306a36Sopenharmony_ci lmac->link_up = false; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (lmac->is_sgmii) 62262306a36Sopenharmony_ci bgx_sgmii_change_link_state(lmac); 62362306a36Sopenharmony_ci else 62462306a36Sopenharmony_ci bgx_xaui_check_link(lmac); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ciu64 bgx_get_rx_stats(int node, int bgx_idx, int lmac, int idx) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct bgx *bgx; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci bgx = get_bgx(node, bgx_idx); 63262306a36Sopenharmony_ci if (!bgx) 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (idx > 8) 63662306a36Sopenharmony_ci lmac = 0; 63762306a36Sopenharmony_ci return bgx_reg_read(bgx, lmac, BGX_CMRX_RX_STAT0 + (idx * 8)); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_rx_stats); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ciu64 bgx_get_tx_stats(int node, int bgx_idx, int lmac, int idx) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct bgx *bgx; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci bgx = get_bgx(node, bgx_idx); 64662306a36Sopenharmony_ci if (!bgx) 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return bgx_reg_read(bgx, lmac, BGX_CMRX_TX_STAT0 + (idx * 8)); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_get_tx_stats); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* Configure BGX LMAC in internal loopback mode */ 65462306a36Sopenharmony_civoid bgx_lmac_internal_loopback(int node, int bgx_idx, 65562306a36Sopenharmony_ci int lmac_idx, bool enable) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct bgx *bgx; 65862306a36Sopenharmony_ci struct lmac *lmac; 65962306a36Sopenharmony_ci u64 cfg; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci bgx = get_bgx(node, bgx_idx); 66262306a36Sopenharmony_ci if (!bgx) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci lmac = &bgx->lmac[lmac_idx]; 66662306a36Sopenharmony_ci if (lmac->is_sgmii) { 66762306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL); 66862306a36Sopenharmony_ci if (enable) 66962306a36Sopenharmony_ci cfg |= PCS_MRX_CTL_LOOPBACK1; 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci cfg &= ~PCS_MRX_CTL_LOOPBACK1; 67262306a36Sopenharmony_ci bgx_reg_write(bgx, lmac_idx, BGX_GMP_PCS_MRX_CTL, cfg); 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmac_idx, BGX_SPUX_CONTROL1); 67562306a36Sopenharmony_ci if (enable) 67662306a36Sopenharmony_ci cfg |= SPU_CTL_LOOPBACK; 67762306a36Sopenharmony_ci else 67862306a36Sopenharmony_ci cfg &= ~SPU_CTL_LOOPBACK; 67962306a36Sopenharmony_ci bgx_reg_write(bgx, lmac_idx, BGX_SPUX_CONTROL1, cfg); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ciEXPORT_SYMBOL(bgx_lmac_internal_loopback); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int bgx_lmac_sgmii_init(struct bgx *bgx, struct lmac *lmac) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int lmacid = lmac->lmacid; 68762306a36Sopenharmony_ci u64 cfg; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_THRESH, 0x30); 69062306a36Sopenharmony_ci /* max packet size */ 69162306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_RXX_JABBER, MAX_FRAME_SIZE); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Disable frame alignment if using preamble */ 69462306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); 69562306a36Sopenharmony_ci if (cfg & 1) 69662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_SGMII_CTL, 0); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Enable lmac */ 69962306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* PCS reset */ 70262306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_RESET); 70362306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, 70462306a36Sopenharmony_ci PCS_MRX_CTL_RESET, true)) { 70562306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX PCS reset not completed\n"); 70662306a36Sopenharmony_ci return -1; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* power down, reset autoneg, autoneg enable */ 71062306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MRX_CTL); 71162306a36Sopenharmony_ci cfg &= ~PCS_MRX_CTL_PWR_DN; 71262306a36Sopenharmony_ci cfg |= PCS_MRX_CTL_RST_AN; 71362306a36Sopenharmony_ci if (lmac->phydev) { 71462306a36Sopenharmony_ci cfg |= PCS_MRX_CTL_AN_EN; 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci /* In scenarios where PHY driver is not present or it's a 71762306a36Sopenharmony_ci * non-standard PHY, FW sets AN_EN to inform Linux driver 71862306a36Sopenharmony_ci * to do auto-neg and link polling or not. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci if (cfg & PCS_MRX_CTL_AN_EN) 72162306a36Sopenharmony_ci lmac->autoneg = true; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MRX_CTL, cfg); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (lmac->lmac_type == BGX_MODE_QSGMII) { 72662306a36Sopenharmony_ci /* Disable disparity check for QSGMII */ 72762306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL); 72862306a36Sopenharmony_ci cfg &= ~PCS_MISC_CTL_DISP_EN; 72962306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_GMP_PCS_MISCX_CTL, cfg); 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if ((lmac->lmac_type == BGX_MODE_SGMII) && lmac->phydev) { 73462306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_GMP_PCS_MRX_STATUS, 73562306a36Sopenharmony_ci PCS_MRX_STATUS_AN_CPT, false)) { 73662306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX AN_CPT not completed\n"); 73762306a36Sopenharmony_ci return -1; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int bgx_lmac_xaui_init(struct bgx *bgx, struct lmac *lmac) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci u64 cfg; 74762306a36Sopenharmony_ci int lmacid = lmac->lmacid; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Reset SPU */ 75062306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET); 75162306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) { 75262306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n"); 75362306a36Sopenharmony_ci return -1; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Disable LMAC */ 75762306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); 75862306a36Sopenharmony_ci cfg &= ~CMR_EN; 75962306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER); 76262306a36Sopenharmony_ci /* Set interleaved running disparity for RXAUI */ 76362306a36Sopenharmony_ci if (lmac->lmac_type == BGX_MODE_RXAUI) 76462306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SPUX_MISC_CONTROL, 76562306a36Sopenharmony_ci SPU_MISC_CTL_INTLV_RDISP); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* Clear receive packet disable */ 76862306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL); 76962306a36Sopenharmony_ci cfg &= ~SPU_MISC_CTL_RX_DIS; 77062306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* clear all interrupts */ 77362306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_INT); 77462306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_RX_INT, cfg); 77562306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_INT); 77662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_INT, cfg); 77762306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); 77862306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (lmac->use_training) { 78162306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LP_CUP, 0x00); 78262306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_CUP, 0x00); 78362306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_LD_REP, 0x00); 78462306a36Sopenharmony_ci /* training enable */ 78562306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, 78662306a36Sopenharmony_ci BGX_SPUX_BR_PMD_CRTL, SPU_PMD_CRTL_TRAIN_EN); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* Append FCS to each packet */ 79062306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, SMU_TX_APPEND_FCS_D); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Disable forward error correction */ 79362306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_FEC_CONTROL); 79462306a36Sopenharmony_ci cfg &= ~SPU_FEC_CTL_FEC_EN; 79562306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_FEC_CONTROL, cfg); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* Disable autoneg */ 79862306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_CONTROL); 79962306a36Sopenharmony_ci cfg = cfg & ~(SPU_AN_CTL_AN_EN | SPU_AN_CTL_XNP_EN); 80062306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_CONTROL, cfg); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_AN_ADV); 80362306a36Sopenharmony_ci if (lmac->lmac_type == BGX_MODE_10G_KR) 80462306a36Sopenharmony_ci cfg |= (1 << 23); 80562306a36Sopenharmony_ci else if (lmac->lmac_type == BGX_MODE_40G_KR) 80662306a36Sopenharmony_ci cfg |= (1 << 24); 80762306a36Sopenharmony_ci else 80862306a36Sopenharmony_ci cfg &= ~((1 << 23) | (1 << 24)); 80962306a36Sopenharmony_ci cfg = cfg & (~((1ULL << 25) | (1ULL << 22) | (1ULL << 12))); 81062306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_AN_ADV, cfg); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, 0, BGX_SPU_DBG_CONTROL); 81362306a36Sopenharmony_ci cfg &= ~SPU_DBG_CTL_AN_ARB_LINK_CHK_EN; 81462306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_SPU_DBG_CONTROL, cfg); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Enable lmac */ 81762306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_CONTROL1); 82062306a36Sopenharmony_ci cfg &= ~SPU_CTL_LOW_POWER; 82162306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_CONTROL1, cfg); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_CTL); 82462306a36Sopenharmony_ci cfg &= ~SMU_TX_CTL_UNI_EN; 82562306a36Sopenharmony_ci cfg |= SMU_TX_CTL_DIC_EN; 82662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_CTL, cfg); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Enable receive and transmission of pause frames */ 82962306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_CBFC_CTL, ((0xffffULL << 32) | 83062306a36Sopenharmony_ci BCK_EN | DRP_EN | TX_EN | RX_EN)); 83162306a36Sopenharmony_ci /* Configure pause time and interval */ 83262306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, 83362306a36Sopenharmony_ci BGX_SMUX_TX_PAUSE_PKT_TIME, DEFAULT_PAUSE_TIME); 83462306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL); 83562306a36Sopenharmony_ci cfg &= ~0xFFFFull; 83662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_PKT_INTERVAL, 83762306a36Sopenharmony_ci cfg | (DEFAULT_PAUSE_TIME - 0x1000)); 83862306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_PAUSE_ZERO, 0x01); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* take lmac_count into account */ 84162306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_THRESH, (0x100 - 1)); 84262306a36Sopenharmony_ci /* max packet size */ 84362306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SMUX_RX_JABBER, MAX_FRAME_SIZE); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic int bgx_xaui_check_link(struct lmac *lmac) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct bgx *bgx = lmac->bgx; 85162306a36Sopenharmony_ci int lmacid = lmac->lmacid; 85262306a36Sopenharmony_ci int lmac_type = lmac->lmac_type; 85362306a36Sopenharmony_ci u64 cfg; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (lmac->use_training) { 85662306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); 85762306a36Sopenharmony_ci if (!(cfg & (1ull << 13))) { 85862306a36Sopenharmony_ci cfg = (1ull << 13) | (1ull << 14); 85962306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); 86062306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL); 86162306a36Sopenharmony_ci cfg |= (1ull << 0); 86262306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL, cfg); 86362306a36Sopenharmony_ci return -1; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci /* wait for PCS to come out of reset */ 86862306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_CONTROL1, SPU_CTL_RESET, true)) { 86962306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX SPU reset not completed\n"); 87062306a36Sopenharmony_ci return -1; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if ((lmac_type == BGX_MODE_10G_KR) || (lmac_type == BGX_MODE_XFI) || 87462306a36Sopenharmony_ci (lmac_type == BGX_MODE_40G_KR) || (lmac_type == BGX_MODE_XLAUI)) { 87562306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BR_STATUS1, 87662306a36Sopenharmony_ci SPU_BR_STATUS_BLK_LOCK, false)) { 87762306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, 87862306a36Sopenharmony_ci "SPU_BR_STATUS_BLK_LOCK not completed\n"); 87962306a36Sopenharmony_ci return -1; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } else { 88262306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_BX_STATUS, 88362306a36Sopenharmony_ci SPU_BX_STATUS_RX_ALIGN, false)) { 88462306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, 88562306a36Sopenharmony_ci "SPU_BX_STATUS_RX_ALIGN not completed\n"); 88662306a36Sopenharmony_ci return -1; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* Clear rcvflt bit (latching high) and read it back */ 89162306a36Sopenharmony_ci if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) 89262306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, 89362306a36Sopenharmony_ci BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT); 89462306a36Sopenharmony_ci if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) { 89562306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "Receive fault, retry training\n"); 89662306a36Sopenharmony_ci if (lmac->use_training) { 89762306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_INT); 89862306a36Sopenharmony_ci if (!(cfg & (1ull << 13))) { 89962306a36Sopenharmony_ci cfg = (1ull << 13) | (1ull << 14); 90062306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SPUX_INT, cfg); 90162306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, 90262306a36Sopenharmony_ci BGX_SPUX_BR_PMD_CRTL); 90362306a36Sopenharmony_ci cfg |= (1ull << 0); 90462306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, 90562306a36Sopenharmony_ci BGX_SPUX_BR_PMD_CRTL, cfg); 90662306a36Sopenharmony_ci return -1; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci return -1; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* Wait for BGX RX to be idle */ 91362306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) { 91462306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "SMU RX not idle\n"); 91562306a36Sopenharmony_ci return -1; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* Wait for BGX TX to be idle */ 91962306a36Sopenharmony_ci if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_TX_IDLE, false)) { 92062306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "SMU TX not idle\n"); 92162306a36Sopenharmony_ci return -1; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Check for MAC RX faults */ 92562306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_CTL); 92662306a36Sopenharmony_ci /* 0 - Link is okay, 1 - Local fault, 2 - Remote fault */ 92762306a36Sopenharmony_ci cfg &= SMU_RX_CTL_STATUS; 92862306a36Sopenharmony_ci if (!cfg) 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Rx local/remote fault seen. 93262306a36Sopenharmony_ci * Do lmac reinit to see if condition recovers 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci bgx_lmac_xaui_init(bgx, lmac); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return -1; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void bgx_poll_for_sgmii_link(struct lmac *lmac) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci u64 pcs_link, an_result; 94262306a36Sopenharmony_ci u8 speed; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid, 94562306a36Sopenharmony_ci BGX_GMP_PCS_MRX_STATUS); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /*Link state bit is sticky, read it again*/ 94862306a36Sopenharmony_ci if (!(pcs_link & PCS_MRX_STATUS_LINK)) 94962306a36Sopenharmony_ci pcs_link = bgx_reg_read(lmac->bgx, lmac->lmacid, 95062306a36Sopenharmony_ci BGX_GMP_PCS_MRX_STATUS); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_GMP_PCS_MRX_STATUS, 95362306a36Sopenharmony_ci PCS_MRX_STATUS_AN_CPT, false)) { 95462306a36Sopenharmony_ci lmac->link_up = false; 95562306a36Sopenharmony_ci lmac->last_speed = SPEED_UNKNOWN; 95662306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_UNKNOWN; 95762306a36Sopenharmony_ci goto next_poll; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci lmac->link_up = ((pcs_link & PCS_MRX_STATUS_LINK) != 0) ? true : false; 96162306a36Sopenharmony_ci an_result = bgx_reg_read(lmac->bgx, lmac->lmacid, 96262306a36Sopenharmony_ci BGX_GMP_PCS_ANX_AN_RESULTS); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci speed = (an_result >> 3) & 0x3; 96562306a36Sopenharmony_ci lmac->last_duplex = (an_result >> 1) & 0x1; 96662306a36Sopenharmony_ci switch (speed) { 96762306a36Sopenharmony_ci case 0: 96862306a36Sopenharmony_ci lmac->last_speed = SPEED_10; 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci case 1: 97162306a36Sopenharmony_ci lmac->last_speed = SPEED_100; 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci case 2: 97462306a36Sopenharmony_ci lmac->last_speed = SPEED_1000; 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci default: 97762306a36Sopenharmony_ci lmac->link_up = false; 97862306a36Sopenharmony_ci lmac->last_speed = SPEED_UNKNOWN; 97962306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_UNKNOWN; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cinext_poll: 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (lmac->last_link != lmac->link_up) { 98662306a36Sopenharmony_ci if (lmac->link_up) 98762306a36Sopenharmony_ci bgx_sgmii_change_link_state(lmac); 98862306a36Sopenharmony_ci lmac->last_link = lmac->link_up; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 3); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic void bgx_poll_for_link(struct work_struct *work) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct lmac *lmac; 99762306a36Sopenharmony_ci u64 spu_link, smu_link; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci lmac = container_of(work, struct lmac, dwork.work); 100062306a36Sopenharmony_ci if (lmac->is_sgmii) { 100162306a36Sopenharmony_ci bgx_poll_for_sgmii_link(lmac); 100262306a36Sopenharmony_ci return; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci /* Receive link is latching low. Force it high and verify it */ 100662306a36Sopenharmony_ci bgx_reg_modify(lmac->bgx, lmac->lmacid, 100762306a36Sopenharmony_ci BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK); 100862306a36Sopenharmony_ci bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1, 100962306a36Sopenharmony_ci SPU_STATUS1_RCV_LNK, false); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci spu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1); 101262306a36Sopenharmony_ci smu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if ((spu_link & SPU_STATUS1_RCV_LNK) && 101562306a36Sopenharmony_ci !(smu_link & SMU_RX_CTL_STATUS)) { 101662306a36Sopenharmony_ci lmac->link_up = true; 101762306a36Sopenharmony_ci if (lmac->lmac_type == BGX_MODE_XLAUI) 101862306a36Sopenharmony_ci lmac->last_speed = SPEED_40000; 101962306a36Sopenharmony_ci else 102062306a36Sopenharmony_ci lmac->last_speed = SPEED_10000; 102162306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_FULL; 102262306a36Sopenharmony_ci } else { 102362306a36Sopenharmony_ci lmac->link_up = false; 102462306a36Sopenharmony_ci lmac->last_speed = SPEED_UNKNOWN; 102562306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_UNKNOWN; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (lmac->last_link != lmac->link_up) { 102962306a36Sopenharmony_ci if (lmac->link_up) { 103062306a36Sopenharmony_ci if (bgx_xaui_check_link(lmac)) { 103162306a36Sopenharmony_ci /* Errors, clear link_up state */ 103262306a36Sopenharmony_ci lmac->link_up = false; 103362306a36Sopenharmony_ci lmac->last_speed = SPEED_UNKNOWN; 103462306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_UNKNOWN; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci lmac->last_link = lmac->link_up; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 2); 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int phy_interface_mode(u8 lmac_type) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci if (lmac_type == BGX_MODE_QSGMII) 104662306a36Sopenharmony_ci return PHY_INTERFACE_MODE_QSGMII; 104762306a36Sopenharmony_ci if (lmac_type == BGX_MODE_RGMII) 104862306a36Sopenharmony_ci return PHY_INTERFACE_MODE_RGMII_RXID; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return PHY_INTERFACE_MODE_SGMII; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic int bgx_lmac_enable(struct bgx *bgx, u8 lmacid) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci struct lmac *lmac; 105662306a36Sopenharmony_ci u64 cfg; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 105962306a36Sopenharmony_ci lmac->bgx = bgx; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if ((lmac->lmac_type == BGX_MODE_SGMII) || 106262306a36Sopenharmony_ci (lmac->lmac_type == BGX_MODE_QSGMII) || 106362306a36Sopenharmony_ci (lmac->lmac_type == BGX_MODE_RGMII)) { 106462306a36Sopenharmony_ci lmac->is_sgmii = true; 106562306a36Sopenharmony_ci if (bgx_lmac_sgmii_init(bgx, lmac)) 106662306a36Sopenharmony_ci return -1; 106762306a36Sopenharmony_ci } else { 106862306a36Sopenharmony_ci lmac->is_sgmii = false; 106962306a36Sopenharmony_ci if (bgx_lmac_xaui_init(bgx, lmac)) 107062306a36Sopenharmony_ci return -1; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (lmac->is_sgmii) { 107462306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND); 107562306a36Sopenharmony_ci cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */ 107662306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_GMP_GMI_TXX_APPEND, cfg); 107762306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_GMP_GMI_TXX_MIN_PKT, 60 - 1); 107862306a36Sopenharmony_ci } else { 107962306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_TX_APPEND); 108062306a36Sopenharmony_ci cfg |= ((1ull << 2) | (1ull << 1)); /* FCS and PAD */ 108162306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_SMUX_TX_APPEND, cfg); 108262306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_SMUX_TX_MIN_PKT, 60 + 4); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* actual number of filters available to exact LMAC */ 108662306a36Sopenharmony_ci lmac->dmacs_count = (RX_DMAC_COUNT / bgx->lmac_count); 108762306a36Sopenharmony_ci lmac->dmacs = kcalloc(lmac->dmacs_count, sizeof(*lmac->dmacs), 108862306a36Sopenharmony_ci GFP_KERNEL); 108962306a36Sopenharmony_ci if (!lmac->dmacs) 109062306a36Sopenharmony_ci return -ENOMEM; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* Enable lmac */ 109362306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Restore default cfg, incase low level firmware changed it */ 109662306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if ((lmac->lmac_type != BGX_MODE_XFI) && 109962306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_XLAUI) && 110062306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_40G_KR) && 110162306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_10G_KR)) { 110262306a36Sopenharmony_ci if (!lmac->phydev) { 110362306a36Sopenharmony_ci if (lmac->autoneg) { 110462306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, 110562306a36Sopenharmony_ci BGX_GMP_PCS_LINKX_TIMER, 110662306a36Sopenharmony_ci PCS_LINKX_TIMER_COUNT); 110762306a36Sopenharmony_ci goto poll; 110862306a36Sopenharmony_ci } else { 110962306a36Sopenharmony_ci /* Default to below link speed and duplex */ 111062306a36Sopenharmony_ci lmac->link_up = true; 111162306a36Sopenharmony_ci lmac->last_speed = SPEED_1000; 111262306a36Sopenharmony_ci lmac->last_duplex = DUPLEX_FULL; 111362306a36Sopenharmony_ci bgx_sgmii_change_link_state(lmac); 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci lmac->phydev->dev_flags = 0; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (phy_connect_direct(&lmac->netdev, lmac->phydev, 112062306a36Sopenharmony_ci bgx_lmac_handler, 112162306a36Sopenharmony_ci phy_interface_mode(lmac->lmac_type))) 112262306a36Sopenharmony_ci return -ENODEV; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci phy_start(lmac->phydev); 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cipoll: 112962306a36Sopenharmony_ci lmac->check_link = alloc_ordered_workqueue("check_link", WQ_MEM_RECLAIM); 113062306a36Sopenharmony_ci if (!lmac->check_link) 113162306a36Sopenharmony_ci return -ENOMEM; 113262306a36Sopenharmony_ci INIT_DELAYED_WORK(&lmac->dwork, bgx_poll_for_link); 113362306a36Sopenharmony_ci queue_delayed_work(lmac->check_link, &lmac->dwork, 0); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci return 0; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic void bgx_lmac_disable(struct bgx *bgx, u8 lmacid) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci struct lmac *lmac; 114162306a36Sopenharmony_ci u64 cfg; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 114462306a36Sopenharmony_ci if (lmac->check_link) { 114562306a36Sopenharmony_ci /* Destroy work queue */ 114662306a36Sopenharmony_ci cancel_delayed_work_sync(&lmac->dwork); 114762306a36Sopenharmony_ci destroy_workqueue(lmac->check_link); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* Disable packet reception */ 115162306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); 115262306a36Sopenharmony_ci cfg &= ~CMR_PKT_RX_EN; 115362306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* Give chance for Rx/Tx FIFO to get drained */ 115662306a36Sopenharmony_ci bgx_poll_reg(bgx, lmacid, BGX_CMRX_RX_FIFO_LEN, (u64)0x1FFF, true); 115762306a36Sopenharmony_ci bgx_poll_reg(bgx, lmacid, BGX_CMRX_TX_FIFO_LEN, (u64)0x3FFF, true); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* Disable packet transmission */ 116062306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); 116162306a36Sopenharmony_ci cfg &= ~CMR_PKT_TX_EN; 116262306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* Disable serdes lanes */ 116562306a36Sopenharmony_ci if (!lmac->is_sgmii) 116662306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, 116762306a36Sopenharmony_ci BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER); 116862306a36Sopenharmony_ci else 116962306a36Sopenharmony_ci bgx_reg_modify(bgx, lmacid, 117062306a36Sopenharmony_ci BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_PWR_DN); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* Disable LMAC */ 117362306a36Sopenharmony_ci cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG); 117462306a36Sopenharmony_ci cfg &= ~CMR_EN; 117562306a36Sopenharmony_ci bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci bgx_flush_dmac_cam_filter(bgx, lmacid); 117862306a36Sopenharmony_ci kfree(lmac->dmacs); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if ((lmac->lmac_type != BGX_MODE_XFI) && 118162306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_XLAUI) && 118262306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_40G_KR) && 118362306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_10G_KR) && lmac->phydev) 118462306a36Sopenharmony_ci phy_disconnect(lmac->phydev); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci lmac->phydev = NULL; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic void bgx_init_hw(struct bgx *bgx) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci int i; 119262306a36Sopenharmony_ci struct lmac *lmac; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci bgx_reg_modify(bgx, 0, BGX_CMR_GLOBAL_CFG, CMR_GLOBAL_CFG_FCS_STRIP); 119562306a36Sopenharmony_ci if (bgx_reg_read(bgx, 0, BGX_CMR_BIST_STATUS)) 119662306a36Sopenharmony_ci dev_err(&bgx->pdev->dev, "BGX%d BIST failed\n", bgx->bgx_id); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Set lmac type and lane2serdes mapping */ 119962306a36Sopenharmony_ci for (i = 0; i < bgx->lmac_count; i++) { 120062306a36Sopenharmony_ci lmac = &bgx->lmac[i]; 120162306a36Sopenharmony_ci bgx_reg_write(bgx, i, BGX_CMRX_CFG, 120262306a36Sopenharmony_ci (lmac->lmac_type << 8) | lmac->lane_to_sds); 120362306a36Sopenharmony_ci bgx->lmac[i].lmacid_bd = lmac_count; 120462306a36Sopenharmony_ci lmac_count++; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_TX_LMACS, bgx->lmac_count); 120862306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_RX_LMACS, bgx->lmac_count); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* Set the backpressure AND mask */ 121162306a36Sopenharmony_ci for (i = 0; i < bgx->lmac_count; i++) 121262306a36Sopenharmony_ci bgx_reg_modify(bgx, 0, BGX_CMR_CHAN_MSK_AND, 121362306a36Sopenharmony_ci ((1ULL << MAX_BGX_CHANS_PER_LMAC) - 1) << 121462306a36Sopenharmony_ci (i * MAX_BGX_CHANS_PER_LMAC)); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Disable all MAC filtering */ 121762306a36Sopenharmony_ci for (i = 0; i < RX_DMAC_COUNT; i++) 121862306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_RX_DMACX_CAM + (i * 8), 0x00); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Disable MAC steering (NCSI traffic) */ 122162306a36Sopenharmony_ci for (i = 0; i < RX_TRAFFIC_STEER_RULE_COUNT; i++) 122262306a36Sopenharmony_ci bgx_reg_write(bgx, 0, BGX_CMR_RX_STEERING + (i * 8), 0x00); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic u8 bgx_get_lane2sds_cfg(struct bgx *bgx, struct lmac *lmac) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci return (u8)(bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG) & 0xFF); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void bgx_print_qlm_mode(struct bgx *bgx, u8 lmacid) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct device *dev = &bgx->pdev->dev; 123362306a36Sopenharmony_ci struct lmac *lmac; 123462306a36Sopenharmony_ci char str[27]; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (!bgx->is_dlm && lmacid) 123762306a36Sopenharmony_ci return; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci lmac = &bgx->lmac[lmacid]; 124062306a36Sopenharmony_ci if (!bgx->is_dlm) 124162306a36Sopenharmony_ci sprintf(str, "BGX%d QLM mode", bgx->bgx_id); 124262306a36Sopenharmony_ci else 124362306a36Sopenharmony_ci sprintf(str, "BGX%d LMAC%d mode", bgx->bgx_id, lmacid); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci switch (lmac->lmac_type) { 124662306a36Sopenharmony_ci case BGX_MODE_SGMII: 124762306a36Sopenharmony_ci dev_info(dev, "%s: SGMII\n", (char *)str); 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci case BGX_MODE_XAUI: 125062306a36Sopenharmony_ci dev_info(dev, "%s: XAUI\n", (char *)str); 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci case BGX_MODE_RXAUI: 125362306a36Sopenharmony_ci dev_info(dev, "%s: RXAUI\n", (char *)str); 125462306a36Sopenharmony_ci break; 125562306a36Sopenharmony_ci case BGX_MODE_XFI: 125662306a36Sopenharmony_ci if (!lmac->use_training) 125762306a36Sopenharmony_ci dev_info(dev, "%s: XFI\n", (char *)str); 125862306a36Sopenharmony_ci else 125962306a36Sopenharmony_ci dev_info(dev, "%s: 10G_KR\n", (char *)str); 126062306a36Sopenharmony_ci break; 126162306a36Sopenharmony_ci case BGX_MODE_XLAUI: 126262306a36Sopenharmony_ci if (!lmac->use_training) 126362306a36Sopenharmony_ci dev_info(dev, "%s: XLAUI\n", (char *)str); 126462306a36Sopenharmony_ci else 126562306a36Sopenharmony_ci dev_info(dev, "%s: 40G_KR4\n", (char *)str); 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci case BGX_MODE_QSGMII: 126862306a36Sopenharmony_ci dev_info(dev, "%s: QSGMII\n", (char *)str); 126962306a36Sopenharmony_ci break; 127062306a36Sopenharmony_ci case BGX_MODE_RGMII: 127162306a36Sopenharmony_ci dev_info(dev, "%s: RGMII\n", (char *)str); 127262306a36Sopenharmony_ci break; 127362306a36Sopenharmony_ci case BGX_MODE_INVALID: 127462306a36Sopenharmony_ci /* Nothing to do */ 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic void lmac_set_lane2sds(struct bgx *bgx, struct lmac *lmac) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci switch (lmac->lmac_type) { 128262306a36Sopenharmony_ci case BGX_MODE_SGMII: 128362306a36Sopenharmony_ci case BGX_MODE_XFI: 128462306a36Sopenharmony_ci lmac->lane_to_sds = lmac->lmacid; 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci case BGX_MODE_XAUI: 128762306a36Sopenharmony_ci case BGX_MODE_XLAUI: 128862306a36Sopenharmony_ci case BGX_MODE_RGMII: 128962306a36Sopenharmony_ci lmac->lane_to_sds = 0xE4; 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci case BGX_MODE_RXAUI: 129262306a36Sopenharmony_ci lmac->lane_to_sds = (lmac->lmacid) ? 0xE : 0x4; 129362306a36Sopenharmony_ci break; 129462306a36Sopenharmony_ci case BGX_MODE_QSGMII: 129562306a36Sopenharmony_ci /* There is no way to determine if DLM0/2 is QSGMII or 129662306a36Sopenharmony_ci * DLM1/3 is configured to QSGMII as bootloader will 129762306a36Sopenharmony_ci * configure all LMACs, so take whatever is configured 129862306a36Sopenharmony_ci * by low level firmware. 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci lmac->lane_to_sds = bgx_get_lane2sds_cfg(bgx, lmac); 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci default: 130362306a36Sopenharmony_ci lmac->lane_to_sds = 0; 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic void lmac_set_training(struct bgx *bgx, struct lmac *lmac, int lmacid) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci if ((lmac->lmac_type != BGX_MODE_10G_KR) && 131162306a36Sopenharmony_ci (lmac->lmac_type != BGX_MODE_40G_KR)) { 131262306a36Sopenharmony_ci lmac->use_training = false; 131362306a36Sopenharmony_ci return; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci lmac->use_training = bgx_reg_read(bgx, lmacid, BGX_SPUX_BR_PMD_CRTL) & 131762306a36Sopenharmony_ci SPU_PMD_CRTL_TRAIN_EN; 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic void bgx_set_lmac_config(struct bgx *bgx, u8 idx) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci struct lmac *lmac; 132362306a36Sopenharmony_ci u64 cmr_cfg; 132462306a36Sopenharmony_ci u8 lmac_type; 132562306a36Sopenharmony_ci u8 lane_to_sds; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci lmac = &bgx->lmac[idx]; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (!bgx->is_dlm || bgx->is_rgx) { 133062306a36Sopenharmony_ci /* Read LMAC0 type to figure out QLM mode 133162306a36Sopenharmony_ci * This is configured by low level firmware 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci cmr_cfg = bgx_reg_read(bgx, 0, BGX_CMRX_CFG); 133462306a36Sopenharmony_ci lmac->lmac_type = (cmr_cfg >> 8) & 0x07; 133562306a36Sopenharmony_ci if (bgx->is_rgx) 133662306a36Sopenharmony_ci lmac->lmac_type = BGX_MODE_RGMII; 133762306a36Sopenharmony_ci lmac_set_training(bgx, lmac, 0); 133862306a36Sopenharmony_ci lmac_set_lane2sds(bgx, lmac); 133962306a36Sopenharmony_ci return; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* For DLMs or SLMs on 80/81/83xx so many lane configurations 134362306a36Sopenharmony_ci * are possible and vary across boards. Also Kernel doesn't have 134462306a36Sopenharmony_ci * any way to identify board type/info and since firmware does, 134562306a36Sopenharmony_ci * just take lmac type and serdes lane config as is. 134662306a36Sopenharmony_ci */ 134762306a36Sopenharmony_ci cmr_cfg = bgx_reg_read(bgx, idx, BGX_CMRX_CFG); 134862306a36Sopenharmony_ci lmac_type = (u8)((cmr_cfg >> 8) & 0x07); 134962306a36Sopenharmony_ci lane_to_sds = (u8)(cmr_cfg & 0xFF); 135062306a36Sopenharmony_ci /* Check if config is reset value */ 135162306a36Sopenharmony_ci if ((lmac_type == 0) && (lane_to_sds == 0xE4)) 135262306a36Sopenharmony_ci lmac->lmac_type = BGX_MODE_INVALID; 135362306a36Sopenharmony_ci else 135462306a36Sopenharmony_ci lmac->lmac_type = lmac_type; 135562306a36Sopenharmony_ci lmac->lane_to_sds = lane_to_sds; 135662306a36Sopenharmony_ci lmac_set_training(bgx, lmac, lmac->lmacid); 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic void bgx_get_qlm_mode(struct bgx *bgx) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct lmac *lmac; 136262306a36Sopenharmony_ci u8 idx; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Init all LMAC's type to invalid */ 136562306a36Sopenharmony_ci for (idx = 0; idx < bgx->max_lmac; idx++) { 136662306a36Sopenharmony_ci lmac = &bgx->lmac[idx]; 136762306a36Sopenharmony_ci lmac->lmacid = idx; 136862306a36Sopenharmony_ci lmac->lmac_type = BGX_MODE_INVALID; 136962306a36Sopenharmony_ci lmac->use_training = false; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* It is assumed that low level firmware sets this value */ 137362306a36Sopenharmony_ci bgx->lmac_count = bgx_reg_read(bgx, 0, BGX_CMR_RX_LMACS) & 0x7; 137462306a36Sopenharmony_ci if (bgx->lmac_count > bgx->max_lmac) 137562306a36Sopenharmony_ci bgx->lmac_count = bgx->max_lmac; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci for (idx = 0; idx < bgx->lmac_count; idx++) { 137862306a36Sopenharmony_ci bgx_set_lmac_config(bgx, idx); 137962306a36Sopenharmony_ci bgx_print_qlm_mode(bgx, idx); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci#ifdef CONFIG_ACPI 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cistatic int acpi_get_mac_address(struct device *dev, struct acpi_device *adev, 138662306a36Sopenharmony_ci u8 *dst) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 138962306a36Sopenharmony_ci int ret; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci ret = fwnode_get_mac_address(acpi_fwnode_handle(adev), mac); 139262306a36Sopenharmony_ci if (ret) { 139362306a36Sopenharmony_ci dev_err(dev, "MAC address invalid: %pM\n", mac); 139462306a36Sopenharmony_ci return -EINVAL; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci dev_info(dev, "MAC address set to: %pM\n", mac); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci ether_addr_copy(dst, mac); 140062306a36Sopenharmony_ci return 0; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci/* Currently only sets the MAC address. */ 140462306a36Sopenharmony_cistatic acpi_status bgx_acpi_register_phy(acpi_handle handle, 140562306a36Sopenharmony_ci u32 lvl, void *context, void **rv) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci struct bgx *bgx = context; 140862306a36Sopenharmony_ci struct device *dev = &bgx->pdev->dev; 140962306a36Sopenharmony_ci struct acpi_device *adev; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci adev = acpi_fetch_acpi_dev(handle); 141262306a36Sopenharmony_ci if (!adev) 141362306a36Sopenharmony_ci goto out; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci acpi_get_mac_address(dev, adev, bgx->lmac[bgx->acpi_lmac_idx].mac); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci SET_NETDEV_DEV(&bgx->lmac[bgx->acpi_lmac_idx].netdev, dev); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci bgx->lmac[bgx->acpi_lmac_idx].lmacid = bgx->acpi_lmac_idx; 142062306a36Sopenharmony_ci bgx->acpi_lmac_idx++; /* move to next LMAC */ 142162306a36Sopenharmony_ciout: 142262306a36Sopenharmony_ci return AE_OK; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic acpi_status bgx_acpi_match_id(acpi_handle handle, u32 lvl, 142662306a36Sopenharmony_ci void *context, void **ret_val) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; 142962306a36Sopenharmony_ci struct bgx *bgx = context; 143062306a36Sopenharmony_ci char bgx_sel[5]; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci snprintf(bgx_sel, 5, "BGX%d", bgx->bgx_id); 143362306a36Sopenharmony_ci if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &string))) { 143462306a36Sopenharmony_ci pr_warn("Invalid link device\n"); 143562306a36Sopenharmony_ci return AE_OK; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (strncmp(string.pointer, bgx_sel, 4)) { 143962306a36Sopenharmony_ci kfree(string.pointer); 144062306a36Sopenharmony_ci return AE_OK; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 144462306a36Sopenharmony_ci bgx_acpi_register_phy, NULL, bgx, NULL); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci kfree(string.pointer); 144762306a36Sopenharmony_ci return AE_CTRL_TERMINATE; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int bgx_init_acpi_phy(struct bgx *bgx) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci acpi_get_devices(NULL, bgx_acpi_match_id, bgx, (void **)NULL); 145362306a36Sopenharmony_ci return 0; 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci#else 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_cistatic int bgx_init_acpi_phy(struct bgx *bgx) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci return -ENODEV; 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci#endif /* CONFIG_ACPI */ 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF_MDIO) 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic int bgx_init_of_phy(struct bgx *bgx) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct fwnode_handle *fwn; 147062306a36Sopenharmony_ci struct device_node *node = NULL; 147162306a36Sopenharmony_ci u8 lmac = 0; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci device_for_each_child_node(&bgx->pdev->dev, fwn) { 147462306a36Sopenharmony_ci struct phy_device *pd; 147562306a36Sopenharmony_ci struct device_node *phy_np; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci /* Should always be an OF node. But if it is not, we 147862306a36Sopenharmony_ci * cannot handle it, so exit the loop. 147962306a36Sopenharmony_ci */ 148062306a36Sopenharmony_ci node = to_of_node(fwn); 148162306a36Sopenharmony_ci if (!node) 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci of_get_mac_address(node, bgx->lmac[lmac].mac); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci SET_NETDEV_DEV(&bgx->lmac[lmac].netdev, &bgx->pdev->dev); 148762306a36Sopenharmony_ci bgx->lmac[lmac].lmacid = lmac; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci phy_np = of_parse_phandle(node, "phy-handle", 0); 149062306a36Sopenharmony_ci /* If there is no phy or defective firmware presents 149162306a36Sopenharmony_ci * this cortina phy, for which there is no driver 149262306a36Sopenharmony_ci * support, ignore it. 149362306a36Sopenharmony_ci */ 149462306a36Sopenharmony_ci if (phy_np && 149562306a36Sopenharmony_ci !of_device_is_compatible(phy_np, "cortina,cs4223-slice")) { 149662306a36Sopenharmony_ci /* Wait until the phy drivers are available */ 149762306a36Sopenharmony_ci pd = of_phy_find_device(phy_np); 149862306a36Sopenharmony_ci if (!pd) 149962306a36Sopenharmony_ci goto defer; 150062306a36Sopenharmony_ci bgx->lmac[lmac].phydev = pd; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci lmac++; 150462306a36Sopenharmony_ci if (lmac == bgx->max_lmac) { 150562306a36Sopenharmony_ci of_node_put(node); 150662306a36Sopenharmony_ci break; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci return 0; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cidefer: 151262306a36Sopenharmony_ci /* We are bailing out, try not to leak device reference counts 151362306a36Sopenharmony_ci * for phy devices we may have already found. 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci while (lmac) { 151662306a36Sopenharmony_ci if (bgx->lmac[lmac].phydev) { 151762306a36Sopenharmony_ci put_device(&bgx->lmac[lmac].phydev->mdio.dev); 151862306a36Sopenharmony_ci bgx->lmac[lmac].phydev = NULL; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci lmac--; 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci of_node_put(node); 152362306a36Sopenharmony_ci return -EPROBE_DEFER; 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci#else 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int bgx_init_of_phy(struct bgx *bgx) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci return -ENODEV; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci#endif /* CONFIG_OF_MDIO */ 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_cistatic int bgx_init_phy(struct bgx *bgx) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci if (!acpi_disabled) 153862306a36Sopenharmony_ci return bgx_init_acpi_phy(bgx); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci return bgx_init_of_phy(bgx); 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic irqreturn_t bgx_intr_handler(int irq, void *data) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci struct bgx *bgx = (struct bgx *)data; 154662306a36Sopenharmony_ci u64 status, val; 154762306a36Sopenharmony_ci int lmac; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci for (lmac = 0; lmac < bgx->lmac_count; lmac++) { 155062306a36Sopenharmony_ci status = bgx_reg_read(bgx, lmac, BGX_GMP_GMI_TXX_INT); 155162306a36Sopenharmony_ci if (status & GMI_TXX_INT_UNDFLW) { 155262306a36Sopenharmony_ci pci_err(bgx->pdev, "BGX%d lmac%d UNDFLW\n", 155362306a36Sopenharmony_ci bgx->bgx_id, lmac); 155462306a36Sopenharmony_ci val = bgx_reg_read(bgx, lmac, BGX_CMRX_CFG); 155562306a36Sopenharmony_ci val &= ~CMR_EN; 155662306a36Sopenharmony_ci bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val); 155762306a36Sopenharmony_ci val |= CMR_EN; 155862306a36Sopenharmony_ci bgx_reg_write(bgx, lmac, BGX_CMRX_CFG, val); 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci /* clear interrupts */ 156162306a36Sopenharmony_ci bgx_reg_write(bgx, lmac, BGX_GMP_GMI_TXX_INT, status); 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci return IRQ_HANDLED; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic void bgx_register_intr(struct pci_dev *pdev) 156862306a36Sopenharmony_ci{ 156962306a36Sopenharmony_ci struct bgx *bgx = pci_get_drvdata(pdev); 157062306a36Sopenharmony_ci int ret; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(pdev, BGX_LMAC_VEC_OFFSET, 157362306a36Sopenharmony_ci BGX_LMAC_VEC_OFFSET, PCI_IRQ_ALL_TYPES); 157462306a36Sopenharmony_ci if (ret < 0) { 157562306a36Sopenharmony_ci pci_err(pdev, "Req for #%d msix vectors failed\n", 157662306a36Sopenharmony_ci BGX_LMAC_VEC_OFFSET); 157762306a36Sopenharmony_ci return; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci ret = pci_request_irq(pdev, GMPX_GMI_TX_INT, bgx_intr_handler, NULL, 158062306a36Sopenharmony_ci bgx, "BGX%d", bgx->bgx_id); 158162306a36Sopenharmony_ci if (ret) 158262306a36Sopenharmony_ci pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); 158362306a36Sopenharmony_ci} 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cistatic int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci int err; 158862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 158962306a36Sopenharmony_ci struct bgx *bgx = NULL; 159062306a36Sopenharmony_ci u8 lmac; 159162306a36Sopenharmony_ci u16 sdevid; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci bgx = devm_kzalloc(dev, sizeof(*bgx), GFP_KERNEL); 159462306a36Sopenharmony_ci if (!bgx) 159562306a36Sopenharmony_ci return -ENOMEM; 159662306a36Sopenharmony_ci bgx->pdev = pdev; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci pci_set_drvdata(pdev, bgx); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci err = pcim_enable_device(pdev); 160162306a36Sopenharmony_ci if (err) { 160262306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 160362306a36Sopenharmony_ci return dev_err_probe(dev, err, "Failed to enable PCI device\n"); 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 160762306a36Sopenharmony_ci if (err) { 160862306a36Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 160962306a36Sopenharmony_ci goto err_disable_device; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci /* MAP configuration registers */ 161362306a36Sopenharmony_ci bgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); 161462306a36Sopenharmony_ci if (!bgx->reg_base) { 161562306a36Sopenharmony_ci dev_err(dev, "BGX: Cannot map CSR memory space, aborting\n"); 161662306a36Sopenharmony_ci err = -ENOMEM; 161762306a36Sopenharmony_ci goto err_release_regions; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci set_max_bgx_per_node(pdev); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_DEVICE_ID, &sdevid); 162362306a36Sopenharmony_ci if (sdevid != PCI_DEVICE_ID_THUNDER_RGX) { 162462306a36Sopenharmony_ci bgx->bgx_id = (pci_resource_start(pdev, 162562306a36Sopenharmony_ci PCI_CFG_REG_BAR_NUM) >> 24) & BGX_ID_MASK; 162662306a36Sopenharmony_ci bgx->bgx_id += nic_get_node_id(pdev) * max_bgx_per_node; 162762306a36Sopenharmony_ci bgx->max_lmac = MAX_LMAC_PER_BGX; 162862306a36Sopenharmony_ci bgx_vnic[bgx->bgx_id] = bgx; 162962306a36Sopenharmony_ci } else { 163062306a36Sopenharmony_ci bgx->is_rgx = true; 163162306a36Sopenharmony_ci bgx->max_lmac = 1; 163262306a36Sopenharmony_ci bgx->bgx_id = MAX_BGX_PER_CN81XX - 1; 163362306a36Sopenharmony_ci bgx_vnic[bgx->bgx_id] = bgx; 163462306a36Sopenharmony_ci xcv_init_hw(); 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* On 81xx all are DLMs and on 83xx there are 3 BGX QLMs and one 163862306a36Sopenharmony_ci * BGX i.e BGX2 can be split across 2 DLMs. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdevid); 164162306a36Sopenharmony_ci if ((sdevid == PCI_SUBSYS_DEVID_81XX_BGX) || 164262306a36Sopenharmony_ci ((sdevid == PCI_SUBSYS_DEVID_83XX_BGX) && (bgx->bgx_id == 2))) 164362306a36Sopenharmony_ci bgx->is_dlm = true; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci bgx_get_qlm_mode(bgx); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci err = bgx_init_phy(bgx); 164862306a36Sopenharmony_ci if (err) 164962306a36Sopenharmony_ci goto err_enable; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci bgx_init_hw(bgx); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci bgx_register_intr(pdev); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci /* Enable all LMACs */ 165662306a36Sopenharmony_ci for (lmac = 0; lmac < bgx->lmac_count; lmac++) { 165762306a36Sopenharmony_ci err = bgx_lmac_enable(bgx, lmac); 165862306a36Sopenharmony_ci if (err) { 165962306a36Sopenharmony_ci dev_err(dev, "BGX%d failed to enable lmac%d\n", 166062306a36Sopenharmony_ci bgx->bgx_id, lmac); 166162306a36Sopenharmony_ci while (lmac) 166262306a36Sopenharmony_ci bgx_lmac_disable(bgx, --lmac); 166362306a36Sopenharmony_ci goto err_enable; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci return 0; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cierr_enable: 167062306a36Sopenharmony_ci bgx_vnic[bgx->bgx_id] = NULL; 167162306a36Sopenharmony_ci pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); 167262306a36Sopenharmony_cierr_release_regions: 167362306a36Sopenharmony_ci pci_release_regions(pdev); 167462306a36Sopenharmony_cierr_disable_device: 167562306a36Sopenharmony_ci pci_disable_device(pdev); 167662306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 167762306a36Sopenharmony_ci return err; 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_cistatic void bgx_remove(struct pci_dev *pdev) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci struct bgx *bgx = pci_get_drvdata(pdev); 168362306a36Sopenharmony_ci u8 lmac; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci /* Disable all LMACs */ 168662306a36Sopenharmony_ci for (lmac = 0; lmac < bgx->lmac_count; lmac++) 168762306a36Sopenharmony_ci bgx_lmac_disable(bgx, lmac); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci pci_free_irq(pdev, GMPX_GMI_TX_INT, bgx); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci bgx_vnic[bgx->bgx_id] = NULL; 169262306a36Sopenharmony_ci pci_release_regions(pdev); 169362306a36Sopenharmony_ci pci_disable_device(pdev); 169462306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic struct pci_driver bgx_driver = { 169862306a36Sopenharmony_ci .name = DRV_NAME, 169962306a36Sopenharmony_ci .id_table = bgx_id_table, 170062306a36Sopenharmony_ci .probe = bgx_probe, 170162306a36Sopenharmony_ci .remove = bgx_remove, 170262306a36Sopenharmony_ci}; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic int __init bgx_init_module(void) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci return pci_register_driver(&bgx_driver); 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_cistatic void __exit bgx_cleanup_module(void) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci pci_unregister_driver(&bgx_driver); 171462306a36Sopenharmony_ci} 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_cimodule_init(bgx_init_module); 171762306a36Sopenharmony_cimodule_exit(bgx_cleanup_module); 1718