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