162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Marvell OcteonTx2 CGX driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2018 Marvell. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/acpi.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/phy.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_mdio.h> 1862306a36Sopenharmony_ci#include <linux/of_net.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "cgx.h" 2162306a36Sopenharmony_ci#include "rvu.h" 2262306a36Sopenharmony_ci#include "lmac_common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DRV_NAME "Marvell-CGX/RPM" 2562306a36Sopenharmony_ci#define DRV_STRING "Marvell CGX/RPM Driver" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic LIST_HEAD(cgx_list); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Convert firmware speed encoding to user format(Mbps) */ 3062306a36Sopenharmony_cistatic const u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX] = { 3162306a36Sopenharmony_ci [CGX_LINK_NONE] = 0, 3262306a36Sopenharmony_ci [CGX_LINK_10M] = 10, 3362306a36Sopenharmony_ci [CGX_LINK_100M] = 100, 3462306a36Sopenharmony_ci [CGX_LINK_1G] = 1000, 3562306a36Sopenharmony_ci [CGX_LINK_2HG] = 2500, 3662306a36Sopenharmony_ci [CGX_LINK_5G] = 5000, 3762306a36Sopenharmony_ci [CGX_LINK_10G] = 10000, 3862306a36Sopenharmony_ci [CGX_LINK_20G] = 20000, 3962306a36Sopenharmony_ci [CGX_LINK_25G] = 25000, 4062306a36Sopenharmony_ci [CGX_LINK_40G] = 40000, 4162306a36Sopenharmony_ci [CGX_LINK_50G] = 50000, 4262306a36Sopenharmony_ci [CGX_LINK_80G] = 80000, 4362306a36Sopenharmony_ci [CGX_LINK_100G] = 100000, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Convert firmware lmac type encoding to string */ 4762306a36Sopenharmony_cistatic const char *cgx_lmactype_string[LMAC_MODE_MAX] = { 4862306a36Sopenharmony_ci [LMAC_MODE_SGMII] = "SGMII", 4962306a36Sopenharmony_ci [LMAC_MODE_XAUI] = "XAUI", 5062306a36Sopenharmony_ci [LMAC_MODE_RXAUI] = "RXAUI", 5162306a36Sopenharmony_ci [LMAC_MODE_10G_R] = "10G_R", 5262306a36Sopenharmony_ci [LMAC_MODE_40G_R] = "40G_R", 5362306a36Sopenharmony_ci [LMAC_MODE_QSGMII] = "QSGMII", 5462306a36Sopenharmony_ci [LMAC_MODE_25G_R] = "25G_R", 5562306a36Sopenharmony_ci [LMAC_MODE_50G_R] = "50G_R", 5662306a36Sopenharmony_ci [LMAC_MODE_100G_R] = "100G_R", 5762306a36Sopenharmony_ci [LMAC_MODE_USXGMII] = "USXGMII", 5862306a36Sopenharmony_ci [LMAC_MODE_USGMII] = "USGMII", 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* CGX PHY management internal APIs */ 6262306a36Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Supported devices */ 6562306a36Sopenharmony_cistatic const struct pci_device_id cgx_id_table[] = { 6662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) }, 6762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM) }, 6862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM) }, 6962306a36Sopenharmony_ci { 0, } /* end of table */ 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cgx_id_table); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic bool is_dev_rpm(void *cgxd) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct cgx *cgx = cgxd; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return (cgx->pdev->device == PCI_DEVID_CN10K_RPM) || 7962306a36Sopenharmony_ci (cgx->pdev->device == PCI_DEVID_CN10KB_RPM); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cibool is_lmac_valid(struct cgx *cgx, int lmac_id) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (!cgx || lmac_id < 0 || lmac_id >= cgx->max_lmac_per_mac) 8562306a36Sopenharmony_ci return false; 8662306a36Sopenharmony_ci return test_bit(lmac_id, &cgx->lmac_bmap); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* Helper function to get sequential index 9062306a36Sopenharmony_ci * given the enabled LMAC of a CGX 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic int get_sequence_id_of_lmac(struct cgx *cgx, int lmac_id) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int tmp, id = 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for_each_set_bit(tmp, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { 9762306a36Sopenharmony_ci if (tmp == lmac_id) 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci id++; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return id; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct mac_ops *get_mac_ops(void *cgxd) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci if (!cgxd) 10862306a36Sopenharmony_ci return cgxd; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return ((struct cgx *)cgxd)->mac_ops; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_civoid cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci writeq(val, cgx->reg_base + (lmac << cgx->mac_ops->lmac_offset) + 11662306a36Sopenharmony_ci offset); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciu64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return readq(cgx->reg_base + (lmac << cgx->mac_ops->lmac_offset) + 12262306a36Sopenharmony_ci offset); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci if (!cgx || lmac_id >= cgx->max_lmac_per_mac) 12862306a36Sopenharmony_ci return NULL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return cgx->lmac_idmap[lmac_id]; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciint cgx_get_cgxcnt_max(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct cgx *cgx_dev; 13662306a36Sopenharmony_ci int idmax = -ENODEV; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci list_for_each_entry(cgx_dev, &cgx_list, cgx_list) 13962306a36Sopenharmony_ci if (cgx_dev->cgx_id > idmax) 14062306a36Sopenharmony_ci idmax = cgx_dev->cgx_id; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (idmax < 0) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return idmax + 1; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint cgx_get_lmac_cnt(void *cgxd) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct cgx *cgx = cgxd; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!cgx) 15362306a36Sopenharmony_ci return -ENODEV; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return cgx->lmac_count; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_civoid *cgx_get_pdata(int cgx_id) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct cgx *cgx_dev; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci list_for_each_entry(cgx_dev, &cgx_list, cgx_list) { 16362306a36Sopenharmony_ci if (cgx_dev->cgx_id == cgx_id) 16462306a36Sopenharmony_ci return cgx_dev; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid cgx_lmac_write(int cgx_id, int lmac_id, u64 offset, u64 val) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Software must not access disabled LMAC registers */ 17462306a36Sopenharmony_ci if (!is_lmac_valid(cgx_dev, lmac_id)) 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci cgx_write(cgx_dev, lmac_id, offset, val); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciu64 cgx_lmac_read(int cgx_id, int lmac_id, u64 offset) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Software must not access disabled LMAC registers */ 18462306a36Sopenharmony_ci if (!is_lmac_valid(cgx_dev, lmac_id)) 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return cgx_read(cgx_dev, lmac_id, offset); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciint cgx_get_cgxid(void *cgxd) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct cgx *cgx = cgxd; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!cgx) 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return cgx->cgx_id; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciu8 cgx_lmac_get_p2x(int cgx_id, int lmac_id) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 20362306a36Sopenharmony_ci u64 cfg; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_CFG); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return (cfg & CMR_P2X_SEL_MASK) >> CMR_P2X_SEL_SHIFT; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* Ensure the required lock for event queue(where asynchronous events are 21162306a36Sopenharmony_ci * posted) is acquired before calling this API. Else an asynchronous event(with 21262306a36Sopenharmony_ci * latest link status) can reach the destination before this function returns 21362306a36Sopenharmony_ci * and could make the link status appear wrong. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ciint cgx_get_link_info(void *cgxd, int lmac_id, 21662306a36Sopenharmony_ci struct cgx_link_user_info *linfo) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgxd); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!lmac) 22162306a36Sopenharmony_ci return -ENODEV; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci *linfo = lmac->link_info; 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 23062306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 23162306a36Sopenharmony_ci struct mac_ops *mac_ops; 23262306a36Sopenharmony_ci int index, id; 23362306a36Sopenharmony_ci u64 cfg; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!lmac) 23662306a36Sopenharmony_ci return -ENODEV; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* access mac_ops to know csr_offset */ 23962306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* copy 6bytes from macaddr */ 24262306a36Sopenharmony_ci /* memcpy(&cfg, mac_addr, 6); */ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci cfg = ether_addr_to_u64(mac_addr); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 25162306a36Sopenharmony_ci cfg | CGX_DMAC_CAM_ADDR_ENABLE | ((u64)lmac_id << 49)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 25462306a36Sopenharmony_ci cfg |= (CGX_DMAC_CTL0_CAM_ENABLE | CGX_DMAC_BCAST_MODE | 25562306a36Sopenharmony_ci CGX_DMAC_MCAST_MODE); 25662306a36Sopenharmony_ci cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciu64 cgx_read_dmac_ctrl(void *cgxd, int lmac_id) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct mac_ops *mac_ops; 26462306a36Sopenharmony_ci struct cgx *cgx = cgxd; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!cgxd || !is_lmac_valid(cgxd, lmac_id)) 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cgx = cgxd; 27062306a36Sopenharmony_ci /* Get mac_ops to know csr offset */ 27162306a36Sopenharmony_ci mac_ops = cgx->mac_ops; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return cgx_read(cgxd, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciu64 cgx_read_dmac_entry(void *cgxd, int index) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct mac_ops *mac_ops; 27962306a36Sopenharmony_ci struct cgx *cgx; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!cgxd) 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci cgx = cgxd; 28562306a36Sopenharmony_ci mac_ops = cgx->mac_ops; 28662306a36Sopenharmony_ci return cgx_read(cgx, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 8))); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciint cgx_lmac_addr_add(u8 cgx_id, u8 lmac_id, u8 *mac_addr) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 29262306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 29362306a36Sopenharmony_ci struct mac_ops *mac_ops; 29462306a36Sopenharmony_ci int index, idx; 29562306a36Sopenharmony_ci u64 cfg = 0; 29662306a36Sopenharmony_ci int id; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!lmac) 29962306a36Sopenharmony_ci return -ENODEV; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 30262306a36Sopenharmony_ci /* Get available index where entry is to be installed */ 30362306a36Sopenharmony_ci idx = rvu_alloc_rsrc(&lmac->mac_to_index_bmap); 30462306a36Sopenharmony_ci if (idx < 0) 30562306a36Sopenharmony_ci return idx; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max + idx; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci cfg = ether_addr_to_u64(mac_addr); 31262306a36Sopenharmony_ci cfg |= CGX_DMAC_CAM_ADDR_ENABLE; 31362306a36Sopenharmony_ci cfg |= ((u64)lmac_id << 49); 31462306a36Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), cfg); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 31762306a36Sopenharmony_ci cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_CAM_ACCEPT); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (is_multicast_ether_addr(mac_addr)) { 32062306a36Sopenharmony_ci cfg &= ~GENMASK_ULL(2, 1); 32162306a36Sopenharmony_ci cfg |= CGX_DMAC_MCAST_MODE_CAM; 32262306a36Sopenharmony_ci lmac->mcast_filters_count++; 32362306a36Sopenharmony_ci } else if (!lmac->mcast_filters_count) { 32462306a36Sopenharmony_ci cfg |= CGX_DMAC_MCAST_MODE; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return idx; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciint cgx_lmac_addr_reset(u8 cgx_id, u8 lmac_id) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 33562306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 33662306a36Sopenharmony_ci struct mac_ops *mac_ops; 33762306a36Sopenharmony_ci u8 index = 0, id; 33862306a36Sopenharmony_ci u64 cfg; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!lmac) 34162306a36Sopenharmony_ci return -ENODEV; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 34462306a36Sopenharmony_ci /* Restore index 0 to its default init value as done during 34562306a36Sopenharmony_ci * cgx_lmac_init 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci set_bit(0, lmac->mac_to_index_bmap.bmap); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max + index; 35262306a36Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Reset CGXX_CMRX_RX_DMAC_CTL0 register to default state */ 35562306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 35662306a36Sopenharmony_ci cfg &= ~CGX_DMAC_CAM_ACCEPT; 35762306a36Sopenharmony_ci cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE); 35862306a36Sopenharmony_ci cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* Allows caller to change macaddress associated with index 36462306a36Sopenharmony_ci * in dmac filter table including index 0 reserved for 36562306a36Sopenharmony_ci * interface mac address 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ciint cgx_lmac_addr_update(u8 cgx_id, u8 lmac_id, u8 *mac_addr, u8 index) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 37062306a36Sopenharmony_ci struct mac_ops *mac_ops; 37162306a36Sopenharmony_ci struct lmac *lmac; 37262306a36Sopenharmony_ci u64 cfg; 37362306a36Sopenharmony_ci int id; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx_dev); 37662306a36Sopenharmony_ci if (!lmac) 37762306a36Sopenharmony_ci return -ENODEV; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 38062306a36Sopenharmony_ci /* Validate the index */ 38162306a36Sopenharmony_ci if (index >= lmac->mac_to_index_bmap.max) 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* ensure index is already set */ 38562306a36Sopenharmony_ci if (!test_bit(index, lmac->mac_to_index_bmap.bmap)) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max + index; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8))); 39362306a36Sopenharmony_ci cfg &= ~CGX_RX_DMAC_ADR_MASK; 39462306a36Sopenharmony_ci cfg |= ether_addr_to_u64(mac_addr); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), cfg); 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ciint cgx_lmac_addr_del(u8 cgx_id, u8 lmac_id, u8 index) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 40362306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 40462306a36Sopenharmony_ci struct mac_ops *mac_ops; 40562306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 40662306a36Sopenharmony_ci u64 cfg; 40762306a36Sopenharmony_ci int id; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!lmac) 41062306a36Sopenharmony_ci return -ENODEV; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 41362306a36Sopenharmony_ci /* Validate the index */ 41462306a36Sopenharmony_ci if (index >= lmac->mac_to_index_bmap.max) 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Skip deletion for reserved index i.e. index 0 */ 41862306a36Sopenharmony_ci if (index == 0) 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci rvu_free_rsrc(&lmac->mac_to_index_bmap, index); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max + index; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Read MAC address to check whether it is ucast or mcast */ 42862306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8))); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci u64_to_ether_addr(cfg, mac); 43162306a36Sopenharmony_ci if (is_multicast_ether_addr(mac)) 43262306a36Sopenharmony_ci lmac->mcast_filters_count--; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!lmac->mcast_filters_count) { 43562306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 43662306a36Sopenharmony_ci cfg &= ~GENMASK_ULL(2, 1); 43762306a36Sopenharmony_ci cfg |= CGX_DMAC_MCAST_MODE; 43862306a36Sopenharmony_ci cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciint cgx_lmac_addr_max_entries_get(u8 cgx_id, u8 lmac_id) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 44962306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (lmac) 45262306a36Sopenharmony_ci return lmac->mac_to_index_bmap.max; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciu64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 46062306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev); 46162306a36Sopenharmony_ci struct mac_ops *mac_ops; 46262306a36Sopenharmony_ci int index; 46362306a36Sopenharmony_ci u64 cfg; 46462306a36Sopenharmony_ci int id; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci mac_ops = cgx_dev->mac_ops; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx_dev, lmac_id); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci index = id * lmac->mac_to_index_bmap.max; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci cfg = cgx_read(cgx_dev, 0, CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8); 47362306a36Sopenharmony_ci return cfg & CGX_RX_DMAC_ADR_MASK; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciint cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct cgx *cgx = cgxd; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 48162306a36Sopenharmony_ci return -ENODEV; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci cgx_write(cgx, lmac_id, cgx->mac_ops->rxid_map_offset, (pkind & 0x3F)); 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic u8 cgx_get_lmac_type(void *cgxd, int lmac_id) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct cgx *cgx = cgxd; 49062306a36Sopenharmony_ci u64 cfg; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 49362306a36Sopenharmony_ci return (cfg >> CGX_LMAC_TYPE_SHIFT) & CGX_LMAC_TYPE_MASK; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic u32 cgx_get_lmac_fifo_len(void *cgxd, int lmac_id) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct cgx *cgx = cgxd; 49962306a36Sopenharmony_ci u8 num_lmacs; 50062306a36Sopenharmony_ci u32 fifo_len; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci fifo_len = cgx->mac_ops->fifo_len; 50362306a36Sopenharmony_ci num_lmacs = cgx->mac_ops->get_nr_lmacs(cgx); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci switch (num_lmacs) { 50662306a36Sopenharmony_ci case 1: 50762306a36Sopenharmony_ci return fifo_len; 50862306a36Sopenharmony_ci case 2: 50962306a36Sopenharmony_ci return fifo_len / 2; 51062306a36Sopenharmony_ci case 3: 51162306a36Sopenharmony_ci /* LMAC0 gets half of the FIFO, reset 1/4th */ 51262306a36Sopenharmony_ci if (lmac_id == 0) 51362306a36Sopenharmony_ci return fifo_len / 2; 51462306a36Sopenharmony_ci return fifo_len / 4; 51562306a36Sopenharmony_ci case 4: 51662306a36Sopenharmony_ci default: 51762306a36Sopenharmony_ci return fifo_len / 4; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* Configure CGX LMAC in internal loopback mode */ 52362306a36Sopenharmony_ciint cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct cgx *cgx = cgxd; 52662306a36Sopenharmony_ci struct lmac *lmac; 52762306a36Sopenharmony_ci u64 cfg; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 53062306a36Sopenharmony_ci return -ENODEV; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 53362306a36Sopenharmony_ci if (lmac->lmac_type == LMAC_MODE_SGMII || 53462306a36Sopenharmony_ci lmac->lmac_type == LMAC_MODE_QSGMII) { 53562306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL); 53662306a36Sopenharmony_ci if (enable) 53762306a36Sopenharmony_ci cfg |= CGXX_GMP_PCS_MRX_CTL_LBK; 53862306a36Sopenharmony_ci else 53962306a36Sopenharmony_ci cfg &= ~CGXX_GMP_PCS_MRX_CTL_LBK; 54062306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL, cfg); 54162306a36Sopenharmony_ci } else { 54262306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SPUX_CONTROL1); 54362306a36Sopenharmony_ci if (enable) 54462306a36Sopenharmony_ci cfg |= CGXX_SPUX_CONTROL1_LBK; 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci cfg &= ~CGXX_SPUX_CONTROL1_LBK; 54762306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1, cfg); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_civoid cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct cgx *cgx = cgx_get_pdata(cgx_id); 55562306a36Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgx); 55662306a36Sopenharmony_ci struct mac_ops *mac_ops; 55762306a36Sopenharmony_ci u16 max_dmac; 55862306a36Sopenharmony_ci int index, i; 55962306a36Sopenharmony_ci u64 cfg = 0; 56062306a36Sopenharmony_ci int id; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (!cgx || !lmac) 56362306a36Sopenharmony_ci return; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci max_dmac = lmac->mac_to_index_bmap.max; 56662306a36Sopenharmony_ci id = get_sequence_id_of_lmac(cgx, lmac_id); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci mac_ops = cgx->mac_ops; 56962306a36Sopenharmony_ci if (enable) { 57062306a36Sopenharmony_ci /* Enable promiscuous mode on LMAC */ 57162306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 57262306a36Sopenharmony_ci cfg &= ~CGX_DMAC_CAM_ACCEPT; 57362306a36Sopenharmony_ci cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE); 57462306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci for (i = 0; i < max_dmac; i++) { 57762306a36Sopenharmony_ci index = id * max_dmac + i; 57862306a36Sopenharmony_ci cfg = cgx_read(cgx, 0, 57962306a36Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8)); 58062306a36Sopenharmony_ci cfg &= ~CGX_DMAC_CAM_ADDR_ENABLE; 58162306a36Sopenharmony_ci cgx_write(cgx, 0, 58262306a36Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8), cfg); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci /* Disable promiscuous mode */ 58662306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 58762306a36Sopenharmony_ci cfg |= CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE; 58862306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 58962306a36Sopenharmony_ci for (i = 0; i < max_dmac; i++) { 59062306a36Sopenharmony_ci index = id * max_dmac + i; 59162306a36Sopenharmony_ci cfg = cgx_read(cgx, 0, 59262306a36Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8)); 59362306a36Sopenharmony_ci if ((cfg & CGX_RX_DMAC_ADR_MASK) != 0) { 59462306a36Sopenharmony_ci cfg |= CGX_DMAC_CAM_ADDR_ENABLE; 59562306a36Sopenharmony_ci cgx_write(cgx, 0, 59662306a36Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + 59762306a36Sopenharmony_ci index * 0x8), 59862306a36Sopenharmony_ci cfg); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int cgx_lmac_get_pause_frm_status(void *cgxd, int lmac_id, 60562306a36Sopenharmony_ci u8 *tx_pause, u8 *rx_pause) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct cgx *cgx = cgxd; 60862306a36Sopenharmony_ci u64 cfg; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (is_dev_rpm(cgx)) 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 61462306a36Sopenharmony_ci return -ENODEV; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 61762306a36Sopenharmony_ci *rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 62062306a36Sopenharmony_ci *tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* Enable or disable forwarding received pause frames to Tx block */ 62562306a36Sopenharmony_civoid cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct cgx *cgx = cgxd; 62862306a36Sopenharmony_ci u8 rx_pause, tx_pause; 62962306a36Sopenharmony_ci bool is_pfc_enabled; 63062306a36Sopenharmony_ci struct lmac *lmac; 63162306a36Sopenharmony_ci u64 cfg; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!cgx) 63462306a36Sopenharmony_ci return; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 63762306a36Sopenharmony_ci if (!lmac) 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Pause frames are not enabled just return */ 64162306a36Sopenharmony_ci if (!bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) 64262306a36Sopenharmony_ci return; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci cgx_lmac_get_pause_frm_status(cgx, lmac_id, &rx_pause, &tx_pause); 64562306a36Sopenharmony_ci is_pfc_enabled = rx_pause ? false : true; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (enable) { 64862306a36Sopenharmony_ci if (!is_pfc_enabled) { 64962306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 65062306a36Sopenharmony_ci cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 65162306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 65462306a36Sopenharmony_ci cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; 65562306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 65662306a36Sopenharmony_ci } else { 65762306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); 65862306a36Sopenharmony_ci cfg |= CGXX_SMUX_CBFC_CTL_BCK_EN; 65962306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci } else { 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!is_pfc_enabled) { 66462306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 66562306a36Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 66662306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 66962306a36Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 67062306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 67162306a36Sopenharmony_ci } else { 67262306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); 67362306a36Sopenharmony_ci cfg &= ~CGXX_SMUX_CBFC_CTL_BCK_EN; 67462306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ciint cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct cgx *cgx = cgxd; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 68462306a36Sopenharmony_ci return -ENODEV; 68562306a36Sopenharmony_ci *rx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_STAT0 + (idx * 8)); 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciint cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct cgx *cgx = cgxd; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 69462306a36Sopenharmony_ci return -ENODEV; 69562306a36Sopenharmony_ci *tx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_TX_STAT0 + (idx * 8)); 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ciu64 cgx_features_get(void *cgxd) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci return ((struct cgx *)cgxd)->hw_features; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int cgx_set_fec_stats_count(struct cgx_link_user_info *linfo) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci if (!linfo->fec) 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci switch (linfo->lmac_type_id) { 71062306a36Sopenharmony_ci case LMAC_MODE_SGMII: 71162306a36Sopenharmony_ci case LMAC_MODE_XAUI: 71262306a36Sopenharmony_ci case LMAC_MODE_RXAUI: 71362306a36Sopenharmony_ci case LMAC_MODE_QSGMII: 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci case LMAC_MODE_10G_R: 71662306a36Sopenharmony_ci case LMAC_MODE_25G_R: 71762306a36Sopenharmony_ci case LMAC_MODE_100G_R: 71862306a36Sopenharmony_ci case LMAC_MODE_USXGMII: 71962306a36Sopenharmony_ci return 1; 72062306a36Sopenharmony_ci case LMAC_MODE_40G_R: 72162306a36Sopenharmony_ci return 4; 72262306a36Sopenharmony_ci case LMAC_MODE_50G_R: 72362306a36Sopenharmony_ci if (linfo->fec == OTX2_FEC_BASER) 72462306a36Sopenharmony_ci return 2; 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci return 1; 72762306a36Sopenharmony_ci default: 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ciint cgx_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci int stats, fec_stats_count = 0; 73562306a36Sopenharmony_ci int corr_reg, uncorr_reg; 73662306a36Sopenharmony_ci struct cgx *cgx = cgxd; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 73962306a36Sopenharmony_ci return -ENODEV; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_NONE) 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci fec_stats_count = 74562306a36Sopenharmony_ci cgx_set_fec_stats_count(&cgx->lmac_idmap[lmac_id]->link_info); 74662306a36Sopenharmony_ci if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) { 74762306a36Sopenharmony_ci corr_reg = CGXX_SPUX_LNX_FEC_CORR_BLOCKS; 74862306a36Sopenharmony_ci uncorr_reg = CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci corr_reg = CGXX_SPUX_RSFEC_CORR; 75162306a36Sopenharmony_ci uncorr_reg = CGXX_SPUX_RSFEC_UNCORR; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci for (stats = 0; stats < fec_stats_count; stats++) { 75462306a36Sopenharmony_ci rsp->fec_corr_blks += 75562306a36Sopenharmony_ci cgx_read(cgx, lmac_id, corr_reg + (stats * 8)); 75662306a36Sopenharmony_ci rsp->fec_uncorr_blks += 75762306a36Sopenharmony_ci cgx_read(cgx, lmac_id, uncorr_reg + (stats * 8)); 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciint cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct cgx *cgx = cgxd; 76562306a36Sopenharmony_ci u64 cfg; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 76862306a36Sopenharmony_ci return -ENODEV; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 77162306a36Sopenharmony_ci if (enable) 77262306a36Sopenharmony_ci cfg |= DATA_PKT_RX_EN | DATA_PKT_TX_EN; 77362306a36Sopenharmony_ci else 77462306a36Sopenharmony_ci cfg &= ~(DATA_PKT_RX_EN | DATA_PKT_TX_EN); 77562306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ciint cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct cgx *cgx = cgxd; 78262306a36Sopenharmony_ci u64 cfg, last; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 78562306a36Sopenharmony_ci return -ENODEV; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 78862306a36Sopenharmony_ci last = cfg; 78962306a36Sopenharmony_ci if (enable) 79062306a36Sopenharmony_ci cfg |= DATA_PKT_TX_EN; 79162306a36Sopenharmony_ci else 79262306a36Sopenharmony_ci cfg &= ~DATA_PKT_TX_EN; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (cfg != last) 79562306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); 79662306a36Sopenharmony_ci return !!(last & DATA_PKT_TX_EN); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int cgx_lmac_enadis_pause_frm(void *cgxd, int lmac_id, 80062306a36Sopenharmony_ci u8 tx_pause, u8 rx_pause) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct cgx *cgx = cgxd; 80362306a36Sopenharmony_ci u64 cfg; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (is_dev_rpm(cgx)) 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 80962306a36Sopenharmony_ci return -ENODEV; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 81262306a36Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 81362306a36Sopenharmony_ci cfg |= rx_pause ? CGX_SMUX_RX_FRM_CTL_CTL_BCK : 0x0; 81462306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 81762306a36Sopenharmony_ci cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; 81862306a36Sopenharmony_ci cfg |= tx_pause ? CGX_SMUX_TX_CTL_L2P_BP_CONV : 0x0; 81962306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP); 82262306a36Sopenharmony_ci if (tx_pause) { 82362306a36Sopenharmony_ci cfg &= ~CGX_CMR_RX_OVR_BP_EN(lmac_id); 82462306a36Sopenharmony_ci } else { 82562306a36Sopenharmony_ci cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); 82662306a36Sopenharmony_ci cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic void cgx_lmac_pause_frm_config(void *cgxd, int lmac_id, bool enable) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct cgx *cgx = cgxd; 83562306a36Sopenharmony_ci u64 cfg; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 83862306a36Sopenharmony_ci return; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (enable) { 84162306a36Sopenharmony_ci /* Set pause time and interval */ 84262306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_TIME, 84362306a36Sopenharmony_ci DEFAULT_PAUSE_TIME); 84462306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL); 84562306a36Sopenharmony_ci cfg &= ~0xFFFFULL; 84662306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL, 84762306a36Sopenharmony_ci cfg | (DEFAULT_PAUSE_TIME / 2)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_TIME, 85062306a36Sopenharmony_ci DEFAULT_PAUSE_TIME); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, 85362306a36Sopenharmony_ci CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL); 85462306a36Sopenharmony_ci cfg &= ~0xFFFFULL; 85562306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL, 85662306a36Sopenharmony_ci cfg | (DEFAULT_PAUSE_TIME / 2)); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* ALL pause frames received are completely ignored */ 86062306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 86162306a36Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 86262306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 86562306a36Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 86662306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Disable pause frames transmission */ 86962306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 87062306a36Sopenharmony_ci cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; 87162306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP); 87462306a36Sopenharmony_ci cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); 87562306a36Sopenharmony_ci cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); 87662306a36Sopenharmony_ci cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Disable all PFC classes by default */ 87962306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); 88062306a36Sopenharmony_ci cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg); 88162306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ciint verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, 88562306a36Sopenharmony_ci int pfvf_idx) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct cgx *cgx = cgxd; 88862306a36Sopenharmony_ci struct lmac *lmac; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 89162306a36Sopenharmony_ci if (!lmac) 89262306a36Sopenharmony_ci return -ENODEV; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (!rx_pause) 89562306a36Sopenharmony_ci clear_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap); 89662306a36Sopenharmony_ci else 89762306a36Sopenharmony_ci set_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (!tx_pause) 90062306a36Sopenharmony_ci clear_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap); 90162306a36Sopenharmony_ci else 90262306a36Sopenharmony_ci set_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* check if other pfvfs are using flow control */ 90562306a36Sopenharmony_ci if (!rx_pause && bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) { 90662306a36Sopenharmony_ci dev_warn(&cgx->pdev->dev, 90762306a36Sopenharmony_ci "Receive Flow control disable not permitted as its used by other PFVFs\n"); 90862306a36Sopenharmony_ci return -EPERM; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (!tx_pause && bitmap_weight(lmac->tx_fc_pfvf_bmap.bmap, lmac->tx_fc_pfvf_bmap.max)) { 91262306a36Sopenharmony_ci dev_warn(&cgx->pdev->dev, 91362306a36Sopenharmony_ci "Transmit Flow control disable not permitted as its used by other PFVFs\n"); 91462306a36Sopenharmony_ci return -EPERM; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ciint cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause, 92162306a36Sopenharmony_ci u8 rx_pause, u16 pfc_en) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct cgx *cgx = cgxd; 92462306a36Sopenharmony_ci u64 cfg; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 92762306a36Sopenharmony_ci return -ENODEV; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* Return as no traffic classes are requested */ 93062306a36Sopenharmony_ci if (tx_pause && !pfc_en) 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); 93462306a36Sopenharmony_ci pfc_en |= FIELD_GET(CGX_PFC_CLASS_MASK, cfg); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (rx_pause) { 93762306a36Sopenharmony_ci cfg |= (CGXX_SMUX_CBFC_CTL_RX_EN | 93862306a36Sopenharmony_ci CGXX_SMUX_CBFC_CTL_BCK_EN | 93962306a36Sopenharmony_ci CGXX_SMUX_CBFC_CTL_DRP_EN); 94062306a36Sopenharmony_ci } else { 94162306a36Sopenharmony_ci cfg &= ~(CGXX_SMUX_CBFC_CTL_RX_EN | 94262306a36Sopenharmony_ci CGXX_SMUX_CBFC_CTL_BCK_EN | 94362306a36Sopenharmony_ci CGXX_SMUX_CBFC_CTL_DRP_EN); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (tx_pause) { 94762306a36Sopenharmony_ci cfg |= CGXX_SMUX_CBFC_CTL_TX_EN; 94862306a36Sopenharmony_ci cfg = FIELD_SET(CGX_PFC_CLASS_MASK, pfc_en, cfg); 94962306a36Sopenharmony_ci } else { 95062306a36Sopenharmony_ci cfg &= ~CGXX_SMUX_CBFC_CTL_TX_EN; 95162306a36Sopenharmony_ci cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg); 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Write source MAC address which will be filled into PFC packet */ 95762306a36Sopenharmony_ci cfg = cgx_lmac_addr_get(cgx->cgx_id, lmac_id); 95862306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_SMAC, cfg); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ciint cgx_lmac_get_pfc_frm_cfg(void *cgxd, int lmac_id, u8 *tx_pause, 96462306a36Sopenharmony_ci u8 *rx_pause) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct cgx *cgx = cgxd; 96762306a36Sopenharmony_ci u64 cfg; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 97062306a36Sopenharmony_ci return -ENODEV; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci *rx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_RX_EN); 97562306a36Sopenharmony_ci *tx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_TX_EN); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_civoid cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct cgx *cgx = cgxd; 98362306a36Sopenharmony_ci u64 cfg; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (!cgx) 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (enable) { 98962306a36Sopenharmony_ci /* Enable inbound PTP timestamping */ 99062306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 99162306a36Sopenharmony_ci cfg |= CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; 99262306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 99562306a36Sopenharmony_ci cfg |= CGX_SMUX_RX_FRM_CTL_PTP_MODE; 99662306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 99762306a36Sopenharmony_ci } else { 99862306a36Sopenharmony_ci /* Disable inbound PTP stamping */ 99962306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 100062306a36Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; 100162306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 100462306a36Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_PTP_MODE; 100562306a36Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci/* CGX Firmware interface low level support */ 101062306a36Sopenharmony_ciint cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci struct cgx *cgx = lmac->cgx; 101362306a36Sopenharmony_ci struct device *dev; 101462306a36Sopenharmony_ci int err = 0; 101562306a36Sopenharmony_ci u64 cmd; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* Ensure no other command is in progress */ 101862306a36Sopenharmony_ci err = mutex_lock_interruptible(&lmac->cmd_lock); 101962306a36Sopenharmony_ci if (err) 102062306a36Sopenharmony_ci return err; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* Ensure command register is free */ 102362306a36Sopenharmony_ci cmd = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); 102462306a36Sopenharmony_ci if (FIELD_GET(CMDREG_OWN, cmd) != CGX_CMD_OWN_NS) { 102562306a36Sopenharmony_ci err = -EBUSY; 102662306a36Sopenharmony_ci goto unlock; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* Update ownership in command request */ 103062306a36Sopenharmony_ci req = FIELD_SET(CMDREG_OWN, CGX_CMD_OWN_FIRMWARE, req); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Mark this lmac as pending, before we start */ 103362306a36Sopenharmony_ci lmac->cmd_pend = true; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* Start command in hardware */ 103662306a36Sopenharmony_ci cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, req); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* Ensure command is completed without errors */ 103962306a36Sopenharmony_ci if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend, 104062306a36Sopenharmony_ci msecs_to_jiffies(CGX_CMD_TIMEOUT))) { 104162306a36Sopenharmony_ci dev = &cgx->pdev->dev; 104262306a36Sopenharmony_ci dev_err(dev, "cgx port %d:%d cmd %lld timeout\n", 104362306a36Sopenharmony_ci cgx->cgx_id, lmac->lmac_id, FIELD_GET(CMDREG_ID, req)); 104462306a36Sopenharmony_ci err = LMAC_AF_ERR_CMD_TIMEOUT; 104562306a36Sopenharmony_ci goto unlock; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* we have a valid command response */ 104962306a36Sopenharmony_ci smp_rmb(); /* Ensure the latest updates are visible */ 105062306a36Sopenharmony_ci *resp = lmac->resp; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ciunlock: 105362306a36Sopenharmony_ci mutex_unlock(&lmac->cmd_lock); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return err; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ciint cgx_fwi_cmd_generic(u64 req, u64 *resp, struct cgx *cgx, int lmac_id) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct lmac *lmac; 106162306a36Sopenharmony_ci int err; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 106462306a36Sopenharmony_ci if (!lmac) 106562306a36Sopenharmony_ci return -ENODEV; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci err = cgx_fwi_cmd_send(req, resp, lmac); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Check for valid response */ 107062306a36Sopenharmony_ci if (!err) { 107162306a36Sopenharmony_ci if (FIELD_GET(EVTREG_STAT, *resp) == CGX_STAT_FAIL) 107262306a36Sopenharmony_ci return -EIO; 107362306a36Sopenharmony_ci else 107462306a36Sopenharmony_ci return 0; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci return err; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int cgx_link_usertable_index_map(int speed) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci switch (speed) { 108362306a36Sopenharmony_ci case SPEED_10: 108462306a36Sopenharmony_ci return CGX_LINK_10M; 108562306a36Sopenharmony_ci case SPEED_100: 108662306a36Sopenharmony_ci return CGX_LINK_100M; 108762306a36Sopenharmony_ci case SPEED_1000: 108862306a36Sopenharmony_ci return CGX_LINK_1G; 108962306a36Sopenharmony_ci case SPEED_2500: 109062306a36Sopenharmony_ci return CGX_LINK_2HG; 109162306a36Sopenharmony_ci case SPEED_5000: 109262306a36Sopenharmony_ci return CGX_LINK_5G; 109362306a36Sopenharmony_ci case SPEED_10000: 109462306a36Sopenharmony_ci return CGX_LINK_10G; 109562306a36Sopenharmony_ci case SPEED_20000: 109662306a36Sopenharmony_ci return CGX_LINK_20G; 109762306a36Sopenharmony_ci case SPEED_25000: 109862306a36Sopenharmony_ci return CGX_LINK_25G; 109962306a36Sopenharmony_ci case SPEED_40000: 110062306a36Sopenharmony_ci return CGX_LINK_40G; 110162306a36Sopenharmony_ci case SPEED_50000: 110262306a36Sopenharmony_ci return CGX_LINK_50G; 110362306a36Sopenharmony_ci case 80000: 110462306a36Sopenharmony_ci return CGX_LINK_80G; 110562306a36Sopenharmony_ci case SPEED_100000: 110662306a36Sopenharmony_ci return CGX_LINK_100G; 110762306a36Sopenharmony_ci case SPEED_UNKNOWN: 110862306a36Sopenharmony_ci return CGX_LINK_NONE; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci return CGX_LINK_NONE; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic void set_mod_args(struct cgx_set_link_mode_args *args, 111462306a36Sopenharmony_ci u32 speed, u8 duplex, u8 autoneg, u64 mode) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci /* Fill default values incase of user did not pass 111762306a36Sopenharmony_ci * valid parameters 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_ci if (args->duplex == DUPLEX_UNKNOWN) 112062306a36Sopenharmony_ci args->duplex = duplex; 112162306a36Sopenharmony_ci if (args->speed == SPEED_UNKNOWN) 112262306a36Sopenharmony_ci args->speed = speed; 112362306a36Sopenharmony_ci if (args->an == AUTONEG_UNKNOWN) 112462306a36Sopenharmony_ci args->an = autoneg; 112562306a36Sopenharmony_ci args->mode = mode; 112662306a36Sopenharmony_ci args->ports = 0; 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic void otx2_map_ethtool_link_modes(u64 bitmask, 113062306a36Sopenharmony_ci struct cgx_set_link_mode_args *args) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci switch (bitmask) { 113362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10baseT_Half_BIT: 113462306a36Sopenharmony_ci set_mod_args(args, 10, 1, 1, BIT_ULL(CGX_MODE_SGMII)); 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10baseT_Full_BIT: 113762306a36Sopenharmony_ci set_mod_args(args, 10, 0, 1, BIT_ULL(CGX_MODE_SGMII)); 113862306a36Sopenharmony_ci break; 113962306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100baseT_Half_BIT: 114062306a36Sopenharmony_ci set_mod_args(args, 100, 1, 1, BIT_ULL(CGX_MODE_SGMII)); 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100baseT_Full_BIT: 114362306a36Sopenharmony_ci set_mod_args(args, 100, 0, 1, BIT_ULL(CGX_MODE_SGMII)); 114462306a36Sopenharmony_ci break; 114562306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_1000baseT_Half_BIT: 114662306a36Sopenharmony_ci set_mod_args(args, 1000, 1, 1, BIT_ULL(CGX_MODE_SGMII)); 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_1000baseT_Full_BIT: 114962306a36Sopenharmony_ci set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_SGMII)); 115062306a36Sopenharmony_ci break; 115162306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_1000baseX_Full_BIT: 115262306a36Sopenharmony_ci set_mod_args(args, 1000, 0, 0, BIT_ULL(CGX_MODE_1000_BASEX)); 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10000baseT_Full_BIT: 115562306a36Sopenharmony_ci set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_QSGMII)); 115662306a36Sopenharmony_ci break; 115762306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10000baseSR_Full_BIT: 115862306a36Sopenharmony_ci set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2C)); 115962306a36Sopenharmony_ci break; 116062306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10000baseLR_Full_BIT: 116162306a36Sopenharmony_ci set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2M)); 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_10000baseKR_Full_BIT: 116462306a36Sopenharmony_ci set_mod_args(args, 10000, 0, 1, BIT_ULL(CGX_MODE_10G_KR)); 116562306a36Sopenharmony_ci break; 116662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT: 116762306a36Sopenharmony_ci set_mod_args(args, 25000, 0, 0, BIT_ULL(CGX_MODE_25G_C2C)); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT: 117062306a36Sopenharmony_ci set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_CR)); 117162306a36Sopenharmony_ci break; 117262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT: 117362306a36Sopenharmony_ci set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_KR)); 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT: 117662306a36Sopenharmony_ci set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2C)); 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT: 117962306a36Sopenharmony_ci set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2M)); 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT: 118262306a36Sopenharmony_ci set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_CR4)); 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT: 118562306a36Sopenharmony_ci set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_KR4)); 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT: 118862306a36Sopenharmony_ci set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2C)); 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT: 119162306a36Sopenharmony_ci set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2M)); 119262306a36Sopenharmony_ci break; 119362306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT: 119462306a36Sopenharmony_ci set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_CR)); 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT: 119762306a36Sopenharmony_ci set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_KR)); 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT: 120062306a36Sopenharmony_ci set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2C)); 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT: 120362306a36Sopenharmony_ci set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2M)); 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT: 120662306a36Sopenharmony_ci set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_CR4)); 120762306a36Sopenharmony_ci break; 120862306a36Sopenharmony_ci case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT: 120962306a36Sopenharmony_ci set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_KR4)); 121062306a36Sopenharmony_ci break; 121162306a36Sopenharmony_ci default: 121262306a36Sopenharmony_ci set_mod_args(args, 0, 1, 0, BIT_ULL(CGX_MODE_MAX)); 121362306a36Sopenharmony_ci break; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic inline void link_status_user_format(u64 lstat, 121862306a36Sopenharmony_ci struct cgx_link_user_info *linfo, 121962306a36Sopenharmony_ci struct cgx *cgx, u8 lmac_id) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci const char *lmac_string; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); 122462306a36Sopenharmony_ci linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); 122562306a36Sopenharmony_ci linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; 122662306a36Sopenharmony_ci linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); 122762306a36Sopenharmony_ci linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); 122862306a36Sopenharmony_ci linfo->lmac_type_id = FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, lstat); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (linfo->lmac_type_id >= LMAC_MODE_MAX) { 123162306a36Sopenharmony_ci dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", 123262306a36Sopenharmony_ci linfo->lmac_type_id, cgx->cgx_id, lmac_id); 123362306a36Sopenharmony_ci strncpy(linfo->lmac_type, "Unknown", LMACTYPE_STR_LEN - 1); 123462306a36Sopenharmony_ci return; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; 123862306a36Sopenharmony_ci strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* Hardware event handlers */ 124262306a36Sopenharmony_cistatic inline void cgx_link_change_handler(u64 lstat, 124362306a36Sopenharmony_ci struct lmac *lmac) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct cgx_link_user_info *linfo; 124662306a36Sopenharmony_ci struct cgx *cgx = lmac->cgx; 124762306a36Sopenharmony_ci struct cgx_link_event event; 124862306a36Sopenharmony_ci struct device *dev; 124962306a36Sopenharmony_ci int err_type; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci dev = &cgx->pdev->dev; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci link_status_user_format(lstat, &event.link_uinfo, cgx, lmac->lmac_id); 125462306a36Sopenharmony_ci err_type = FIELD_GET(RESP_LINKSTAT_ERRTYPE, lstat); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci event.cgx_id = cgx->cgx_id; 125762306a36Sopenharmony_ci event.lmac_id = lmac->lmac_id; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* update the local copy of link status */ 126062306a36Sopenharmony_ci lmac->link_info = event.link_uinfo; 126162306a36Sopenharmony_ci linfo = &lmac->link_info; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (err_type == CGX_ERR_SPEED_CHANGE_INVALID) 126462306a36Sopenharmony_ci return; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* Ensure callback doesn't get unregistered until we finish it */ 126762306a36Sopenharmony_ci spin_lock(&lmac->event_cb_lock); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (!lmac->event_cb.notify_link_chg) { 127062306a36Sopenharmony_ci dev_dbg(dev, "cgx port %d:%d Link change handler null", 127162306a36Sopenharmony_ci cgx->cgx_id, lmac->lmac_id); 127262306a36Sopenharmony_ci if (err_type != CGX_ERR_NONE) { 127362306a36Sopenharmony_ci dev_err(dev, "cgx port %d:%d Link error %d\n", 127462306a36Sopenharmony_ci cgx->cgx_id, lmac->lmac_id, err_type); 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n", 127762306a36Sopenharmony_ci cgx->cgx_id, lmac->lmac_id, 127862306a36Sopenharmony_ci linfo->link_up ? "UP" : "DOWN", linfo->speed); 127962306a36Sopenharmony_ci goto err; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data)) 128362306a36Sopenharmony_ci dev_err(dev, "event notification failure\n"); 128462306a36Sopenharmony_cierr: 128562306a36Sopenharmony_ci spin_unlock(&lmac->event_cb_lock); 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic inline bool cgx_cmdresp_is_linkevent(u64 event) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci u8 id; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci id = FIELD_GET(EVTREG_ID, event); 129362306a36Sopenharmony_ci if (id == CGX_CMD_LINK_BRING_UP || 129462306a36Sopenharmony_ci id == CGX_CMD_LINK_BRING_DOWN || 129562306a36Sopenharmony_ci id == CGX_CMD_MODE_CHANGE) 129662306a36Sopenharmony_ci return true; 129762306a36Sopenharmony_ci else 129862306a36Sopenharmony_ci return false; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic inline bool cgx_event_is_linkevent(u64 event) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci if (FIELD_GET(EVTREG_ID, event) == CGX_EVT_LINK_CHANGE) 130462306a36Sopenharmony_ci return true; 130562306a36Sopenharmony_ci else 130662306a36Sopenharmony_ci return false; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic irqreturn_t cgx_fwi_event_handler(int irq, void *data) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci u64 event, offset, clear_bit; 131262306a36Sopenharmony_ci struct lmac *lmac = data; 131362306a36Sopenharmony_ci struct cgx *cgx; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci cgx = lmac->cgx; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* Clear SW_INT for RPM and CMR_INT for CGX */ 131862306a36Sopenharmony_ci offset = cgx->mac_ops->int_register; 131962306a36Sopenharmony_ci clear_bit = cgx->mac_ops->int_ena_bit; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci event = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (!FIELD_GET(EVTREG_ACK, event)) 132462306a36Sopenharmony_ci return IRQ_NONE; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci switch (FIELD_GET(EVTREG_EVT_TYPE, event)) { 132762306a36Sopenharmony_ci case CGX_EVT_CMD_RESP: 132862306a36Sopenharmony_ci /* Copy the response. Since only one command is active at a 132962306a36Sopenharmony_ci * time, there is no way a response can get overwritten 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_ci lmac->resp = event; 133262306a36Sopenharmony_ci /* Ensure response is updated before thread context starts */ 133362306a36Sopenharmony_ci smp_wmb(); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* There wont be separate events for link change initiated from 133662306a36Sopenharmony_ci * software; Hence report the command responses as events 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci if (cgx_cmdresp_is_linkevent(event)) 133962306a36Sopenharmony_ci cgx_link_change_handler(event, lmac); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* Release thread waiting for completion */ 134262306a36Sopenharmony_ci lmac->cmd_pend = false; 134362306a36Sopenharmony_ci wake_up(&lmac->wq_cmd_cmplt); 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci case CGX_EVT_ASYNC: 134662306a36Sopenharmony_ci if (cgx_event_is_linkevent(event)) 134762306a36Sopenharmony_ci cgx_link_change_handler(event, lmac); 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci /* Any new event or command response will be posted by firmware 135262306a36Sopenharmony_ci * only after the current status is acked. 135362306a36Sopenharmony_ci * Ack the interrupt register as well. 135462306a36Sopenharmony_ci */ 135562306a36Sopenharmony_ci cgx_write(lmac->cgx, lmac->lmac_id, CGX_EVENT_REG, 0); 135662306a36Sopenharmony_ci cgx_write(lmac->cgx, lmac->lmac_id, offset, clear_bit); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return IRQ_HANDLED; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci/* APIs for PHY management using CGX firmware interface */ 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci/* callback registration for hardware events like link change */ 136462306a36Sopenharmony_ciint cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct cgx *cgx = cgxd; 136762306a36Sopenharmony_ci struct lmac *lmac; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 137062306a36Sopenharmony_ci if (!lmac) 137162306a36Sopenharmony_ci return -ENODEV; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci lmac->event_cb = *cb; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci return 0; 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ciint cgx_lmac_evh_unregister(void *cgxd, int lmac_id) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct lmac *lmac; 138162306a36Sopenharmony_ci unsigned long flags; 138262306a36Sopenharmony_ci struct cgx *cgx = cgxd; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 138562306a36Sopenharmony_ci if (!lmac) 138662306a36Sopenharmony_ci return -ENODEV; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci spin_lock_irqsave(&lmac->event_cb_lock, flags); 138962306a36Sopenharmony_ci lmac->event_cb.notify_link_chg = NULL; 139062306a36Sopenharmony_ci lmac->event_cb.data = NULL; 139162306a36Sopenharmony_ci spin_unlock_irqrestore(&lmac->event_cb_lock, flags); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ciint cgx_get_fwdata_base(u64 *base) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci u64 req = 0, resp; 139962306a36Sopenharmony_ci struct cgx *cgx; 140062306a36Sopenharmony_ci int first_lmac; 140162306a36Sopenharmony_ci int err; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci cgx = list_first_entry_or_null(&cgx_list, struct cgx, cgx_list); 140462306a36Sopenharmony_ci if (!cgx) 140562306a36Sopenharmony_ci return -ENXIO; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); 140862306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req); 140962306a36Sopenharmony_ci err = cgx_fwi_cmd_generic(req, &resp, cgx, first_lmac); 141062306a36Sopenharmony_ci if (!err) 141162306a36Sopenharmony_ci *base = FIELD_GET(RESP_FWD_BASE, resp); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci return err; 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ciint cgx_set_link_mode(void *cgxd, struct cgx_set_link_mode_args args, 141762306a36Sopenharmony_ci int cgx_id, int lmac_id) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci struct cgx *cgx = cgxd; 142062306a36Sopenharmony_ci u64 req = 0, resp; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (!cgx) 142362306a36Sopenharmony_ci return -ENODEV; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (args.mode) 142662306a36Sopenharmony_ci otx2_map_ethtool_link_modes(args.mode, &args); 142762306a36Sopenharmony_ci if (!args.speed && args.duplex && !args.an) 142862306a36Sopenharmony_ci return -EINVAL; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_MODE_CHANGE, req); 143162306a36Sopenharmony_ci req = FIELD_SET(CMDMODECHANGE_SPEED, 143262306a36Sopenharmony_ci cgx_link_usertable_index_map(args.speed), req); 143362306a36Sopenharmony_ci req = FIELD_SET(CMDMODECHANGE_DUPLEX, args.duplex, req); 143462306a36Sopenharmony_ci req = FIELD_SET(CMDMODECHANGE_AN, args.an, req); 143562306a36Sopenharmony_ci req = FIELD_SET(CMDMODECHANGE_PORT, args.ports, req); 143662306a36Sopenharmony_ci req = FIELD_SET(CMDMODECHANGE_FLAGS, args.mode, req); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ciint cgx_set_fec(u64 fec, int cgx_id, int lmac_id) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci u64 req = 0, resp; 144362306a36Sopenharmony_ci struct cgx *cgx; 144462306a36Sopenharmony_ci int err = 0; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci cgx = cgx_get_pdata(cgx_id); 144762306a36Sopenharmony_ci if (!cgx) 144862306a36Sopenharmony_ci return -ENXIO; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_SET_FEC, req); 145162306a36Sopenharmony_ci req = FIELD_SET(CMDSETFEC, fec, req); 145262306a36Sopenharmony_ci err = cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); 145362306a36Sopenharmony_ci if (err) 145462306a36Sopenharmony_ci return err; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci cgx->lmac_idmap[lmac_id]->link_info.fec = 145762306a36Sopenharmony_ci FIELD_GET(RESP_LINKSTAT_FEC, resp); 145862306a36Sopenharmony_ci return cgx->lmac_idmap[lmac_id]->link_info.fec; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ciint cgx_get_phy_fec_stats(void *cgxd, int lmac_id) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct cgx *cgx = cgxd; 146462306a36Sopenharmony_ci u64 req = 0, resp; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (!cgx) 146762306a36Sopenharmony_ci return -ENODEV; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_PHY_FEC_STATS, req); 147062306a36Sopenharmony_ci return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci u64 req = 0; 147662306a36Sopenharmony_ci u64 resp; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (enable) { 147962306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req); 148062306a36Sopenharmony_ci /* On CN10K firmware offloads link bring up/down operations to ECP 148162306a36Sopenharmony_ci * On Octeontx2 link operations are handled by firmware itself 148262306a36Sopenharmony_ci * which can cause mbox errors so configure maximum time firmware 148362306a36Sopenharmony_ci * poll for Link as 1000 ms 148462306a36Sopenharmony_ci */ 148562306a36Sopenharmony_ci if (!is_dev_rpm(cgx)) 148662306a36Sopenharmony_ci req = FIELD_SET(LINKCFG_TIMEOUT, 1000, req); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci } else { 148962306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req); 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci int first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); 149762306a36Sopenharmony_ci u64 req = 0; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req); 150062306a36Sopenharmony_ci return cgx_fwi_cmd_generic(req, resp, cgx, first_lmac); 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic int cgx_lmac_verify_fwi_version(struct cgx *cgx) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct device *dev = &cgx->pdev->dev; 150662306a36Sopenharmony_ci int major_ver, minor_ver; 150762306a36Sopenharmony_ci u64 resp; 150862306a36Sopenharmony_ci int err; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (!cgx->lmac_count) 151162306a36Sopenharmony_ci return 0; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci err = cgx_fwi_read_version(&resp, cgx); 151462306a36Sopenharmony_ci if (err) 151562306a36Sopenharmony_ci return err; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci major_ver = FIELD_GET(RESP_MAJOR_VER, resp); 151862306a36Sopenharmony_ci minor_ver = FIELD_GET(RESP_MINOR_VER, resp); 151962306a36Sopenharmony_ci dev_dbg(dev, "Firmware command interface version = %d.%d\n", 152062306a36Sopenharmony_ci major_ver, minor_ver); 152162306a36Sopenharmony_ci if (major_ver != CGX_FIRMWARE_MAJOR_VER) 152262306a36Sopenharmony_ci return -EIO; 152362306a36Sopenharmony_ci else 152462306a36Sopenharmony_ci return 0; 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic void cgx_lmac_linkup_work(struct work_struct *work) 152862306a36Sopenharmony_ci{ 152962306a36Sopenharmony_ci struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work); 153062306a36Sopenharmony_ci struct device *dev = &cgx->pdev->dev; 153162306a36Sopenharmony_ci int i, err; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci /* Do Link up for all the enabled lmacs */ 153462306a36Sopenharmony_ci for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { 153562306a36Sopenharmony_ci err = cgx_fwi_link_change(cgx, i, true); 153662306a36Sopenharmony_ci if (err) 153762306a36Sopenharmony_ci dev_info(dev, "cgx port %d:%d Link up command failed\n", 153862306a36Sopenharmony_ci cgx->cgx_id, i); 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ciint cgx_lmac_linkup_start(void *cgxd) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct cgx *cgx = cgxd; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (!cgx) 154762306a36Sopenharmony_ci return -ENODEV; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci return 0; 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ciint cgx_lmac_reset(void *cgxd, int lmac_id, u8 pf_req_flr) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct cgx *cgx = cgxd; 155762306a36Sopenharmony_ci u64 cfg; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (!is_lmac_valid(cgx, lmac_id)) 156062306a36Sopenharmony_ci return -ENODEV; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* Resetting PFC related CSRs */ 156362306a36Sopenharmony_ci cfg = 0xff; 156462306a36Sopenharmony_ci cgx_write(cgxd, lmac_id, CGXX_CMRX_RX_LOGL_XON, cfg); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (pf_req_flr) 156762306a36Sopenharmony_ci cgx_lmac_internal_loopback(cgxd, lmac_id, false); 156862306a36Sopenharmony_ci return 0; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic int cgx_configure_interrupt(struct cgx *cgx, struct lmac *lmac, 157262306a36Sopenharmony_ci int cnt, bool req_free) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci struct mac_ops *mac_ops = cgx->mac_ops; 157562306a36Sopenharmony_ci u64 offset, ena_bit; 157662306a36Sopenharmony_ci unsigned int irq; 157762306a36Sopenharmony_ci int err; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci irq = pci_irq_vector(cgx->pdev, mac_ops->lmac_fwi + 158062306a36Sopenharmony_ci cnt * mac_ops->irq_offset); 158162306a36Sopenharmony_ci offset = mac_ops->int_set_reg; 158262306a36Sopenharmony_ci ena_bit = mac_ops->int_ena_bit; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (req_free) { 158562306a36Sopenharmony_ci free_irq(irq, lmac); 158662306a36Sopenharmony_ci return 0; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci err = request_irq(irq, cgx_fwi_event_handler, 0, lmac->name, lmac); 159062306a36Sopenharmony_ci if (err) 159162306a36Sopenharmony_ci return err; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci /* Enable interrupt */ 159462306a36Sopenharmony_ci cgx_write(cgx, lmac->lmac_id, offset, ena_bit); 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ciint cgx_get_nr_lmacs(void *cgxd) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci struct cgx *cgx = cgxd; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci return cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0x7ULL; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ciu8 cgx_get_lmacid(void *cgxd, u8 lmac_index) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci struct cgx *cgx = cgxd; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return cgx->lmac_idmap[lmac_index]->lmac_id; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ciunsigned long cgx_get_lmac_bmap(void *cgxd) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct cgx *cgx = cgxd; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci return cgx->lmac_bmap; 161762306a36Sopenharmony_ci} 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_cistatic int cgx_lmac_init(struct cgx *cgx) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct lmac *lmac; 162262306a36Sopenharmony_ci u64 lmac_list; 162362306a36Sopenharmony_ci int i, err; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci /* lmac_list specifies which lmacs are enabled 162662306a36Sopenharmony_ci * when bit n is set to 1, LMAC[n] is enabled 162762306a36Sopenharmony_ci */ 162862306a36Sopenharmony_ci if (cgx->mac_ops->non_contiguous_serdes_lane) { 162962306a36Sopenharmony_ci if (is_dev_rpm2(cgx)) 163062306a36Sopenharmony_ci lmac_list = 163162306a36Sopenharmony_ci cgx_read(cgx, 0, RPM2_CMRX_RX_LMACS) & 0xFFULL; 163262306a36Sopenharmony_ci else 163362306a36Sopenharmony_ci lmac_list = 163462306a36Sopenharmony_ci cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci if (cgx->lmac_count > cgx->max_lmac_per_mac) 163862306a36Sopenharmony_ci cgx->lmac_count = cgx->max_lmac_per_mac; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci for (i = 0; i < cgx->lmac_count; i++) { 164162306a36Sopenharmony_ci lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL); 164262306a36Sopenharmony_ci if (!lmac) 164362306a36Sopenharmony_ci return -ENOMEM; 164462306a36Sopenharmony_ci lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL); 164562306a36Sopenharmony_ci if (!lmac->name) { 164662306a36Sopenharmony_ci err = -ENOMEM; 164762306a36Sopenharmony_ci goto err_lmac_free; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i); 165062306a36Sopenharmony_ci if (cgx->mac_ops->non_contiguous_serdes_lane) { 165162306a36Sopenharmony_ci lmac->lmac_id = __ffs64(lmac_list); 165262306a36Sopenharmony_ci lmac_list &= ~BIT_ULL(lmac->lmac_id); 165362306a36Sopenharmony_ci } else { 165462306a36Sopenharmony_ci lmac->lmac_id = i; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci lmac->cgx = cgx; 165862306a36Sopenharmony_ci lmac->mac_to_index_bmap.max = 165962306a36Sopenharmony_ci cgx->mac_ops->dmac_filter_count / 166062306a36Sopenharmony_ci cgx->lmac_count; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci err = rvu_alloc_bitmap(&lmac->mac_to_index_bmap); 166362306a36Sopenharmony_ci if (err) 166462306a36Sopenharmony_ci goto err_name_free; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* Reserve first entry for default MAC address */ 166762306a36Sopenharmony_ci set_bit(0, lmac->mac_to_index_bmap.bmap); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci lmac->rx_fc_pfvf_bmap.max = 128; 167062306a36Sopenharmony_ci err = rvu_alloc_bitmap(&lmac->rx_fc_pfvf_bmap); 167162306a36Sopenharmony_ci if (err) 167262306a36Sopenharmony_ci goto err_dmac_bmap_free; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci lmac->tx_fc_pfvf_bmap.max = 128; 167562306a36Sopenharmony_ci err = rvu_alloc_bitmap(&lmac->tx_fc_pfvf_bmap); 167662306a36Sopenharmony_ci if (err) 167762306a36Sopenharmony_ci goto err_rx_fc_bmap_free; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci init_waitqueue_head(&lmac->wq_cmd_cmplt); 168062306a36Sopenharmony_ci mutex_init(&lmac->cmd_lock); 168162306a36Sopenharmony_ci spin_lock_init(&lmac->event_cb_lock); 168262306a36Sopenharmony_ci err = cgx_configure_interrupt(cgx, lmac, lmac->lmac_id, false); 168362306a36Sopenharmony_ci if (err) 168462306a36Sopenharmony_ci goto err_bitmap_free; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci /* Add reference */ 168762306a36Sopenharmony_ci cgx->lmac_idmap[lmac->lmac_id] = lmac; 168862306a36Sopenharmony_ci set_bit(lmac->lmac_id, &cgx->lmac_bmap); 168962306a36Sopenharmony_ci cgx->mac_ops->mac_pause_frm_config(cgx, lmac->lmac_id, true); 169062306a36Sopenharmony_ci lmac->lmac_type = cgx->mac_ops->get_lmac_type(cgx, lmac->lmac_id); 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci return cgx_lmac_verify_fwi_version(cgx); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cierr_bitmap_free: 169662306a36Sopenharmony_ci rvu_free_bitmap(&lmac->tx_fc_pfvf_bmap); 169762306a36Sopenharmony_cierr_rx_fc_bmap_free: 169862306a36Sopenharmony_ci rvu_free_bitmap(&lmac->rx_fc_pfvf_bmap); 169962306a36Sopenharmony_cierr_dmac_bmap_free: 170062306a36Sopenharmony_ci rvu_free_bitmap(&lmac->mac_to_index_bmap); 170162306a36Sopenharmony_cierr_name_free: 170262306a36Sopenharmony_ci kfree(lmac->name); 170362306a36Sopenharmony_cierr_lmac_free: 170462306a36Sopenharmony_ci kfree(lmac); 170562306a36Sopenharmony_ci return err; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic int cgx_lmac_exit(struct cgx *cgx) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct lmac *lmac; 171162306a36Sopenharmony_ci int i; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (cgx->cgx_cmd_workq) { 171462306a36Sopenharmony_ci destroy_workqueue(cgx->cgx_cmd_workq); 171562306a36Sopenharmony_ci cgx->cgx_cmd_workq = NULL; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci /* Free all lmac related resources */ 171962306a36Sopenharmony_ci for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { 172062306a36Sopenharmony_ci lmac = cgx->lmac_idmap[i]; 172162306a36Sopenharmony_ci if (!lmac) 172262306a36Sopenharmony_ci continue; 172362306a36Sopenharmony_ci cgx->mac_ops->mac_pause_frm_config(cgx, lmac->lmac_id, false); 172462306a36Sopenharmony_ci cgx_configure_interrupt(cgx, lmac, lmac->lmac_id, true); 172562306a36Sopenharmony_ci kfree(lmac->mac_to_index_bmap.bmap); 172662306a36Sopenharmony_ci kfree(lmac->name); 172762306a36Sopenharmony_ci kfree(lmac); 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci return 0; 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic void cgx_populate_features(struct cgx *cgx) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci u64 cfg; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci cfg = cgx_read(cgx, 0, CGX_CONST); 173862306a36Sopenharmony_ci cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); 173962306a36Sopenharmony_ci cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (is_dev_rpm(cgx)) 174262306a36Sopenharmony_ci cgx->hw_features = (RVU_LMAC_FEAT_DMACF | RVU_MAC_RPM | 174362306a36Sopenharmony_ci RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_PTP); 174462306a36Sopenharmony_ci else 174562306a36Sopenharmony_ci cgx->hw_features = (RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_HIGIG2 | 174662306a36Sopenharmony_ci RVU_LMAC_FEAT_PTP | RVU_LMAC_FEAT_DMACF); 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_cistatic u8 cgx_get_rxid_mapoffset(struct cgx *cgx) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci if (cgx->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10KB_RPM || 175262306a36Sopenharmony_ci is_dev_rpm2(cgx)) 175362306a36Sopenharmony_ci return 0x80; 175462306a36Sopenharmony_ci else 175562306a36Sopenharmony_ci return 0x60; 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_cistatic struct mac_ops cgx_mac_ops = { 175962306a36Sopenharmony_ci .name = "cgx", 176062306a36Sopenharmony_ci .csr_offset = 0, 176162306a36Sopenharmony_ci .lmac_offset = 18, 176262306a36Sopenharmony_ci .int_register = CGXX_CMRX_INT, 176362306a36Sopenharmony_ci .int_set_reg = CGXX_CMRX_INT_ENA_W1S, 176462306a36Sopenharmony_ci .irq_offset = 9, 176562306a36Sopenharmony_ci .int_ena_bit = FW_CGX_INT, 176662306a36Sopenharmony_ci .lmac_fwi = CGX_LMAC_FWI, 176762306a36Sopenharmony_ci .non_contiguous_serdes_lane = false, 176862306a36Sopenharmony_ci .rx_stats_cnt = 9, 176962306a36Sopenharmony_ci .tx_stats_cnt = 18, 177062306a36Sopenharmony_ci .dmac_filter_count = 32, 177162306a36Sopenharmony_ci .get_nr_lmacs = cgx_get_nr_lmacs, 177262306a36Sopenharmony_ci .get_lmac_type = cgx_get_lmac_type, 177362306a36Sopenharmony_ci .lmac_fifo_len = cgx_get_lmac_fifo_len, 177462306a36Sopenharmony_ci .mac_lmac_intl_lbk = cgx_lmac_internal_loopback, 177562306a36Sopenharmony_ci .mac_get_rx_stats = cgx_get_rx_stats, 177662306a36Sopenharmony_ci .mac_get_tx_stats = cgx_get_tx_stats, 177762306a36Sopenharmony_ci .get_fec_stats = cgx_get_fec_stats, 177862306a36Sopenharmony_ci .mac_enadis_rx_pause_fwding = cgx_lmac_enadis_rx_pause_fwding, 177962306a36Sopenharmony_ci .mac_get_pause_frm_status = cgx_lmac_get_pause_frm_status, 178062306a36Sopenharmony_ci .mac_enadis_pause_frm = cgx_lmac_enadis_pause_frm, 178162306a36Sopenharmony_ci .mac_pause_frm_config = cgx_lmac_pause_frm_config, 178262306a36Sopenharmony_ci .mac_enadis_ptp_config = cgx_lmac_ptp_config, 178362306a36Sopenharmony_ci .mac_rx_tx_enable = cgx_lmac_rx_tx_enable, 178462306a36Sopenharmony_ci .mac_tx_enable = cgx_lmac_tx_enable, 178562306a36Sopenharmony_ci .pfc_config = cgx_lmac_pfc_config, 178662306a36Sopenharmony_ci .mac_get_pfc_frm_cfg = cgx_lmac_get_pfc_frm_cfg, 178762306a36Sopenharmony_ci .mac_reset = cgx_lmac_reset, 178862306a36Sopenharmony_ci}; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_cistatic int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) 179162306a36Sopenharmony_ci{ 179262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 179362306a36Sopenharmony_ci struct cgx *cgx; 179462306a36Sopenharmony_ci int err, nvec; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci cgx = devm_kzalloc(dev, sizeof(*cgx), GFP_KERNEL); 179762306a36Sopenharmony_ci if (!cgx) 179862306a36Sopenharmony_ci return -ENOMEM; 179962306a36Sopenharmony_ci cgx->pdev = pdev; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci pci_set_drvdata(pdev, cgx); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci /* Use mac_ops to get MAC specific features */ 180462306a36Sopenharmony_ci if (is_dev_rpm(cgx)) 180562306a36Sopenharmony_ci cgx->mac_ops = rpm_get_mac_ops(cgx); 180662306a36Sopenharmony_ci else 180762306a36Sopenharmony_ci cgx->mac_ops = &cgx_mac_ops; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci cgx->mac_ops->rxid_map_offset = cgx_get_rxid_mapoffset(cgx); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci err = pci_enable_device(pdev); 181262306a36Sopenharmony_ci if (err) { 181362306a36Sopenharmony_ci dev_err(dev, "Failed to enable PCI device\n"); 181462306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 181562306a36Sopenharmony_ci return err; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 181962306a36Sopenharmony_ci if (err) { 182062306a36Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 182162306a36Sopenharmony_ci goto err_disable_device; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* MAP configuration registers */ 182562306a36Sopenharmony_ci cgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); 182662306a36Sopenharmony_ci if (!cgx->reg_base) { 182762306a36Sopenharmony_ci dev_err(dev, "CGX: Cannot map CSR memory space, aborting\n"); 182862306a36Sopenharmony_ci err = -ENOMEM; 182962306a36Sopenharmony_ci goto err_release_regions; 183062306a36Sopenharmony_ci } 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx); 183362306a36Sopenharmony_ci if (!cgx->lmac_count) { 183462306a36Sopenharmony_ci dev_notice(dev, "CGX %d LMAC count is zero, skipping probe\n", cgx->cgx_id); 183562306a36Sopenharmony_ci err = -EOPNOTSUPP; 183662306a36Sopenharmony_ci goto err_release_regions; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci nvec = pci_msix_vec_count(cgx->pdev); 184062306a36Sopenharmony_ci err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX); 184162306a36Sopenharmony_ci if (err < 0 || err != nvec) { 184262306a36Sopenharmony_ci dev_err(dev, "Request for %d msix vectors failed, err %d\n", 184362306a36Sopenharmony_ci nvec, err); 184462306a36Sopenharmony_ci goto err_release_regions; 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24) 184862306a36Sopenharmony_ci & CGX_ID_MASK; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci /* init wq for processing linkup requests */ 185162306a36Sopenharmony_ci INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work); 185262306a36Sopenharmony_ci cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0); 185362306a36Sopenharmony_ci if (!cgx->cgx_cmd_workq) { 185462306a36Sopenharmony_ci dev_err(dev, "alloc workqueue failed for cgx cmd"); 185562306a36Sopenharmony_ci err = -ENOMEM; 185662306a36Sopenharmony_ci goto err_free_irq_vectors; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci list_add(&cgx->cgx_list, &cgx_list); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci cgx_populate_features(cgx); 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci mutex_init(&cgx->lock); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci err = cgx_lmac_init(cgx); 186762306a36Sopenharmony_ci if (err) 186862306a36Sopenharmony_ci goto err_release_lmac; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci return 0; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cierr_release_lmac: 187362306a36Sopenharmony_ci cgx_lmac_exit(cgx); 187462306a36Sopenharmony_ci list_del(&cgx->cgx_list); 187562306a36Sopenharmony_cierr_free_irq_vectors: 187662306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 187762306a36Sopenharmony_cierr_release_regions: 187862306a36Sopenharmony_ci pci_release_regions(pdev); 187962306a36Sopenharmony_cierr_disable_device: 188062306a36Sopenharmony_ci pci_disable_device(pdev); 188162306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 188262306a36Sopenharmony_ci return err; 188362306a36Sopenharmony_ci} 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_cistatic void cgx_remove(struct pci_dev *pdev) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci struct cgx *cgx = pci_get_drvdata(pdev); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci if (cgx) { 189062306a36Sopenharmony_ci cgx_lmac_exit(cgx); 189162306a36Sopenharmony_ci list_del(&cgx->cgx_list); 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 189462306a36Sopenharmony_ci pci_release_regions(pdev); 189562306a36Sopenharmony_ci pci_disable_device(pdev); 189662306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 189762306a36Sopenharmony_ci} 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_cistruct pci_driver cgx_driver = { 190062306a36Sopenharmony_ci .name = DRV_NAME, 190162306a36Sopenharmony_ci .id_table = cgx_id_table, 190262306a36Sopenharmony_ci .probe = cgx_probe, 190362306a36Sopenharmony_ci .remove = cgx_remove, 190462306a36Sopenharmony_ci}; 1905