18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Marvell OcteonTx2 CGX driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Marvell International Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/phy.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 208c2ecf20Sopenharmony_ci#include <linux/of_net.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "cgx.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "octeontx2-cgx" 258c2ecf20Sopenharmony_ci#define DRV_STRING "Marvell OcteonTX2 CGX/MAC Driver" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/** 288c2ecf20Sopenharmony_ci * struct lmac 298c2ecf20Sopenharmony_ci * @wq_cmd_cmplt: waitq to keep the process blocked until cmd completion 308c2ecf20Sopenharmony_ci * @cmd_lock: Lock to serialize the command interface 318c2ecf20Sopenharmony_ci * @resp: command response 328c2ecf20Sopenharmony_ci * @link_info: link related information 338c2ecf20Sopenharmony_ci * @event_cb: callback for linkchange events 348c2ecf20Sopenharmony_ci * @event_cb_lock: lock for serializing callback with unregister 358c2ecf20Sopenharmony_ci * @cmd_pend: flag set before new command is started 368c2ecf20Sopenharmony_ci * flag cleared after command response is received 378c2ecf20Sopenharmony_ci * @cgx: parent cgx port 388c2ecf20Sopenharmony_ci * @lmac_id: lmac port id 398c2ecf20Sopenharmony_ci * @name: lmac port name 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistruct lmac { 428c2ecf20Sopenharmony_ci wait_queue_head_t wq_cmd_cmplt; 438c2ecf20Sopenharmony_ci struct mutex cmd_lock; 448c2ecf20Sopenharmony_ci u64 resp; 458c2ecf20Sopenharmony_ci struct cgx_link_user_info link_info; 468c2ecf20Sopenharmony_ci struct cgx_event_cb event_cb; 478c2ecf20Sopenharmony_ci spinlock_t event_cb_lock; 488c2ecf20Sopenharmony_ci bool cmd_pend; 498c2ecf20Sopenharmony_ci struct cgx *cgx; 508c2ecf20Sopenharmony_ci u8 lmac_id; 518c2ecf20Sopenharmony_ci char *name; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct cgx { 558c2ecf20Sopenharmony_ci void __iomem *reg_base; 568c2ecf20Sopenharmony_ci struct pci_dev *pdev; 578c2ecf20Sopenharmony_ci u8 cgx_id; 588c2ecf20Sopenharmony_ci u8 lmac_count; 598c2ecf20Sopenharmony_ci struct lmac *lmac_idmap[MAX_LMAC_PER_CGX]; 608c2ecf20Sopenharmony_ci struct work_struct cgx_cmd_work; 618c2ecf20Sopenharmony_ci struct workqueue_struct *cgx_cmd_workq; 628c2ecf20Sopenharmony_ci struct list_head cgx_list; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic LIST_HEAD(cgx_list); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Convert firmware speed encoding to user format(Mbps) */ 688c2ecf20Sopenharmony_cistatic u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX]; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Convert firmware lmac type encoding to string */ 718c2ecf20Sopenharmony_cistatic char *cgx_lmactype_string[LMAC_MODE_MAX]; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* CGX PHY management internal APIs */ 748c2ecf20Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Supported devices */ 778c2ecf20Sopenharmony_cistatic const struct pci_device_id cgx_id_table[] = { 788c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) }, 798c2ecf20Sopenharmony_ci { 0, } /* end of table */ 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cgx_id_table); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci writeq(val, cgx->reg_base + (lmac << 18) + offset); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return readq(cgx->reg_base + (lmac << 18) + offset); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= MAX_LMAC_PER_CGX) 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return cgx->lmac_idmap[lmac_id]; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciint cgx_get_cgxcnt_max(void) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct cgx *cgx_dev; 1058c2ecf20Sopenharmony_ci int idmax = -ENODEV; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci list_for_each_entry(cgx_dev, &cgx_list, cgx_list) 1088c2ecf20Sopenharmony_ci if (cgx_dev->cgx_id > idmax) 1098c2ecf20Sopenharmony_ci idmax = cgx_dev->cgx_id; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (idmax < 0) 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return idmax + 1; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciint cgx_get_lmac_cnt(void *cgxd) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!cgx) 1228c2ecf20Sopenharmony_ci return -ENODEV; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return cgx->lmac_count; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid *cgx_get_pdata(int cgx_id) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct cgx *cgx_dev; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci list_for_each_entry(cgx_dev, &cgx_list, cgx_list) { 1328c2ecf20Sopenharmony_ci if (cgx_dev->cgx_id == cgx_id) 1338c2ecf20Sopenharmony_ci return cgx_dev; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci return NULL; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciint cgx_get_cgxid(void *cgxd) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!cgx) 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return cgx->cgx_id; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciu8 cgx_lmac_get_p2x(int cgx_id, int lmac_id) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 1518c2ecf20Sopenharmony_ci u64 cfg; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_CFG); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return (cfg & CMR_P2X_SEL_MASK) >> CMR_P2X_SEL_SHIFT; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* Ensure the required lock for event queue(where asynchronous events are 1598c2ecf20Sopenharmony_ci * posted) is acquired before calling this API. Else an asynchronous event(with 1608c2ecf20Sopenharmony_ci * latest link status) can reach the destination before this function returns 1618c2ecf20Sopenharmony_ci * and could make the link status appear wrong. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ciint cgx_get_link_info(void *cgxd, int lmac_id, 1648c2ecf20Sopenharmony_ci struct cgx_link_user_info *linfo) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct lmac *lmac = lmac_pdata(lmac_id, cgxd); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!lmac) 1698c2ecf20Sopenharmony_ci return -ENODEV; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci *linfo = lmac->link_info; 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic u64 mac2u64 (u8 *mac_addr) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u64 mac = 0; 1788c2ecf20Sopenharmony_ci int index; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (index = ETH_ALEN - 1; index >= 0; index--) 1818c2ecf20Sopenharmony_ci mac |= ((u64)*mac_addr++) << (8 * index); 1828c2ecf20Sopenharmony_ci return mac; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 1888c2ecf20Sopenharmony_ci u64 cfg; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* copy 6bytes from macaddr */ 1918c2ecf20Sopenharmony_ci /* memcpy(&cfg, mac_addr, 6); */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cfg = mac2u64 (mac_addr); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (lmac_id * 0x8)), 1968c2ecf20Sopenharmony_ci cfg | CGX_DMAC_CAM_ADDR_ENABLE | ((u64)lmac_id << 49)); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 1998c2ecf20Sopenharmony_ci cfg |= CGX_DMAC_CTL0_CAM_ENABLE; 2008c2ecf20Sopenharmony_ci cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciu64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct cgx *cgx_dev = cgx_get_pdata(cgx_id); 2088c2ecf20Sopenharmony_ci u64 cfg; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci cfg = cgx_read(cgx_dev, 0, CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8); 2118c2ecf20Sopenharmony_ci return cfg & CGX_RX_DMAC_ADR_MASK; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciint cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 2198c2ecf20Sopenharmony_ci return -ENODEV; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_RX_ID_MAP, (pkind & 0x3F)); 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic inline u8 cgx_get_lmac_type(struct cgx *cgx, int lmac_id) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci u64 cfg; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 2308c2ecf20Sopenharmony_ci return (cfg >> CGX_LMAC_TYPE_SHIFT) & CGX_LMAC_TYPE_MASK; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* Configure CGX LMAC in internal loopback mode */ 2348c2ecf20Sopenharmony_ciint cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 2378c2ecf20Sopenharmony_ci u8 lmac_type; 2388c2ecf20Sopenharmony_ci u64 cfg; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 2418c2ecf20Sopenharmony_ci return -ENODEV; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci lmac_type = cgx_get_lmac_type(cgx, lmac_id); 2448c2ecf20Sopenharmony_ci if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) { 2458c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL); 2468c2ecf20Sopenharmony_ci if (enable) 2478c2ecf20Sopenharmony_ci cfg |= CGXX_GMP_PCS_MRX_CTL_LBK; 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci cfg &= ~CGXX_GMP_PCS_MRX_CTL_LBK; 2508c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL, cfg); 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SPUX_CONTROL1); 2538c2ecf20Sopenharmony_ci if (enable) 2548c2ecf20Sopenharmony_ci cfg |= CGXX_SPUX_CONTROL1_LBK; 2558c2ecf20Sopenharmony_ci else 2568c2ecf20Sopenharmony_ci cfg &= ~CGXX_SPUX_CONTROL1_LBK; 2578c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1, cfg); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct cgx *cgx = cgx_get_pdata(cgx_id); 2658c2ecf20Sopenharmony_ci u64 cfg = 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (!cgx) 2688c2ecf20Sopenharmony_ci return; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (enable) { 2718c2ecf20Sopenharmony_ci /* Enable promiscuous mode on LMAC */ 2728c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 2738c2ecf20Sopenharmony_ci cfg &= ~(CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE); 2748c2ecf20Sopenharmony_ci cfg |= CGX_DMAC_BCAST_MODE; 2758c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, 0, 2788c2ecf20Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8)); 2798c2ecf20Sopenharmony_ci cfg &= ~CGX_DMAC_CAM_ADDR_ENABLE; 2808c2ecf20Sopenharmony_ci cgx_write(cgx, 0, 2818c2ecf20Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8), cfg); 2828c2ecf20Sopenharmony_ci } else { 2838c2ecf20Sopenharmony_ci /* Disable promiscuous mode */ 2848c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0); 2858c2ecf20Sopenharmony_ci cfg |= CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE; 2868c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg); 2878c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, 0, 2888c2ecf20Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8)); 2898c2ecf20Sopenharmony_ci cfg |= CGX_DMAC_CAM_ADDR_ENABLE; 2908c2ecf20Sopenharmony_ci cgx_write(cgx, 0, 2918c2ecf20Sopenharmony_ci (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8), cfg); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* Enable or disable forwarding received pause frames to Tx block */ 2968c2ecf20Sopenharmony_civoid cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 2998c2ecf20Sopenharmony_ci u64 cfg; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!cgx) 3028c2ecf20Sopenharmony_ci return; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (enable) { 3058c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 3068c2ecf20Sopenharmony_ci cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 3078c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 3108c2ecf20Sopenharmony_ci cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; 3118c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 3128c2ecf20Sopenharmony_ci } else { 3138c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 3148c2ecf20Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 3158c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 3188c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 3198c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciint cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 3288c2ecf20Sopenharmony_ci return -ENODEV; 3298c2ecf20Sopenharmony_ci *rx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_STAT0 + (idx * 8)); 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ciint cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 3388c2ecf20Sopenharmony_ci return -ENODEV; 3398c2ecf20Sopenharmony_ci *tx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_TX_STAT0 + (idx * 8)); 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciint cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 3468c2ecf20Sopenharmony_ci u64 cfg; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 3498c2ecf20Sopenharmony_ci return -ENODEV; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 3528c2ecf20Sopenharmony_ci if (enable) 3538c2ecf20Sopenharmony_ci cfg |= DATA_PKT_RX_EN | DATA_PKT_TX_EN; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci cfg &= ~(DATA_PKT_RX_EN | DATA_PKT_TX_EN); 3568c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciint cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 3638c2ecf20Sopenharmony_ci u64 cfg, last; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 3668c2ecf20Sopenharmony_ci return -ENODEV; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG); 3698c2ecf20Sopenharmony_ci last = cfg; 3708c2ecf20Sopenharmony_ci if (enable) 3718c2ecf20Sopenharmony_ci cfg |= DATA_PKT_TX_EN; 3728c2ecf20Sopenharmony_ci else 3738c2ecf20Sopenharmony_ci cfg &= ~DATA_PKT_TX_EN; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (cfg != last) 3768c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg); 3778c2ecf20Sopenharmony_ci return !!(last & DATA_PKT_TX_EN); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint cgx_lmac_get_pause_frm(void *cgxd, int lmac_id, 3818c2ecf20Sopenharmony_ci u8 *tx_pause, u8 *rx_pause) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 3848c2ecf20Sopenharmony_ci u64 cfg; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 3878c2ecf20Sopenharmony_ci return -ENODEV; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 3908c2ecf20Sopenharmony_ci *rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 3938c2ecf20Sopenharmony_ci *tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciint cgx_lmac_set_pause_frm(void *cgxd, int lmac_id, 3988c2ecf20Sopenharmony_ci u8 tx_pause, u8 rx_pause) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 4018c2ecf20Sopenharmony_ci u64 cfg; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 4048c2ecf20Sopenharmony_ci return -ENODEV; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 4078c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 4088c2ecf20Sopenharmony_ci cfg |= rx_pause ? CGX_SMUX_RX_FRM_CTL_CTL_BCK : 0x0; 4098c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 4128c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; 4138c2ecf20Sopenharmony_ci cfg |= tx_pause ? CGX_SMUX_TX_CTL_L2P_BP_CONV : 0x0; 4148c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP); 4178c2ecf20Sopenharmony_ci if (tx_pause) { 4188c2ecf20Sopenharmony_ci cfg &= ~CGX_CMR_RX_OVR_BP_EN(lmac_id); 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); 4218c2ecf20Sopenharmony_ci cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic void cgx_lmac_pause_frm_config(struct cgx *cgx, int lmac_id, bool enable) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci u64 cfg; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!cgx || lmac_id >= cgx->lmac_count) 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci if (enable) { 4348c2ecf20Sopenharmony_ci /* Enable receive pause frames */ 4358c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 4368c2ecf20Sopenharmony_ci cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK; 4378c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 4408c2ecf20Sopenharmony_ci cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 4418c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* Enable pause frames transmission */ 4448c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 4458c2ecf20Sopenharmony_ci cfg |= CGX_SMUX_TX_CTL_L2P_BP_CONV; 4468c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Set pause time and interval */ 4498c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_TIME, 4508c2ecf20Sopenharmony_ci DEFAULT_PAUSE_TIME); 4518c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL); 4528c2ecf20Sopenharmony_ci cfg &= ~0xFFFFULL; 4538c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL, 4548c2ecf20Sopenharmony_ci cfg | (DEFAULT_PAUSE_TIME / 2)); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_TIME, 4578c2ecf20Sopenharmony_ci DEFAULT_PAUSE_TIME); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, 4608c2ecf20Sopenharmony_ci CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL); 4618c2ecf20Sopenharmony_ci cfg &= ~0xFFFFULL; 4628c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL, 4638c2ecf20Sopenharmony_ci cfg | (DEFAULT_PAUSE_TIME / 2)); 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci /* ALL pause frames received are completely ignored */ 4668c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 4678c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK; 4688c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 4718c2ecf20Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK; 4728c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Disable pause frames transmission */ 4758c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL); 4768c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV; 4778c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 4848c2ecf20Sopenharmony_ci u64 cfg; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (!cgx) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (enable) { 4908c2ecf20Sopenharmony_ci /* Enable inbound PTP timestamping */ 4918c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 4928c2ecf20Sopenharmony_ci cfg |= CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; 4938c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 4968c2ecf20Sopenharmony_ci cfg |= CGX_SMUX_RX_FRM_CTL_PTP_MODE; 4978c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci /* Disable inbound PTP stamping */ 5008c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL); 5018c2ecf20Sopenharmony_ci cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE; 5028c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL); 5058c2ecf20Sopenharmony_ci cfg &= ~CGX_SMUX_RX_FRM_CTL_PTP_MODE; 5068c2ecf20Sopenharmony_ci cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* CGX Firmware interface low level support */ 5118c2ecf20Sopenharmony_cistatic int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct cgx *cgx = lmac->cgx; 5148c2ecf20Sopenharmony_ci struct device *dev; 5158c2ecf20Sopenharmony_ci int err = 0; 5168c2ecf20Sopenharmony_ci u64 cmd; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Ensure no other command is in progress */ 5198c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&lmac->cmd_lock); 5208c2ecf20Sopenharmony_ci if (err) 5218c2ecf20Sopenharmony_ci return err; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Ensure command register is free */ 5248c2ecf20Sopenharmony_ci cmd = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); 5258c2ecf20Sopenharmony_ci if (FIELD_GET(CMDREG_OWN, cmd) != CGX_CMD_OWN_NS) { 5268c2ecf20Sopenharmony_ci err = -EBUSY; 5278c2ecf20Sopenharmony_ci goto unlock; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Update ownership in command request */ 5318c2ecf20Sopenharmony_ci req = FIELD_SET(CMDREG_OWN, CGX_CMD_OWN_FIRMWARE, req); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Mark this lmac as pending, before we start */ 5348c2ecf20Sopenharmony_ci lmac->cmd_pend = true; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Start command in hardware */ 5378c2ecf20Sopenharmony_ci cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, req); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Ensure command is completed without errors */ 5408c2ecf20Sopenharmony_ci if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend, 5418c2ecf20Sopenharmony_ci msecs_to_jiffies(CGX_CMD_TIMEOUT))) { 5428c2ecf20Sopenharmony_ci dev = &cgx->pdev->dev; 5438c2ecf20Sopenharmony_ci dev_err(dev, "cgx port %d:%d cmd timeout\n", 5448c2ecf20Sopenharmony_ci cgx->cgx_id, lmac->lmac_id); 5458c2ecf20Sopenharmony_ci err = -EIO; 5468c2ecf20Sopenharmony_ci goto unlock; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* we have a valid command response */ 5508c2ecf20Sopenharmony_ci smp_rmb(); /* Ensure the latest updates are visible */ 5518c2ecf20Sopenharmony_ci *resp = lmac->resp; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciunlock: 5548c2ecf20Sopenharmony_ci mutex_unlock(&lmac->cmd_lock); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return err; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic inline int cgx_fwi_cmd_generic(u64 req, u64 *resp, 5608c2ecf20Sopenharmony_ci struct cgx *cgx, int lmac_id) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct lmac *lmac; 5638c2ecf20Sopenharmony_ci int err; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 5668c2ecf20Sopenharmony_ci if (!lmac) 5678c2ecf20Sopenharmony_ci return -ENODEV; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci err = cgx_fwi_cmd_send(req, resp, lmac); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Check for valid response */ 5728c2ecf20Sopenharmony_ci if (!err) { 5738c2ecf20Sopenharmony_ci if (FIELD_GET(EVTREG_STAT, *resp) == CGX_STAT_FAIL) 5748c2ecf20Sopenharmony_ci return -EIO; 5758c2ecf20Sopenharmony_ci else 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return err; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic inline void cgx_link_usertable_init(void) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_NONE] = 0; 5858c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_10M] = 10; 5868c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_100M] = 100; 5878c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_1G] = 1000; 5888c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_2HG] = 2500; 5898c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_5G] = 5000; 5908c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_10G] = 10000; 5918c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_20G] = 20000; 5928c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_25G] = 25000; 5938c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_40G] = 40000; 5948c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_50G] = 50000; 5958c2ecf20Sopenharmony_ci cgx_speed_mbps[CGX_LINK_100G] = 100000; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_SGMII] = "SGMII"; 5988c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_XAUI] = "XAUI"; 5998c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_RXAUI] = "RXAUI"; 6008c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_10G_R] = "10G_R"; 6018c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_40G_R] = "40G_R"; 6028c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_QSGMII] = "QSGMII"; 6038c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_25G_R] = "25G_R"; 6048c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_50G_R] = "50G_R"; 6058c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_100G_R] = "100G_R"; 6068c2ecf20Sopenharmony_ci cgx_lmactype_string[LMAC_MODE_USXGMII] = "USXGMII"; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic inline void link_status_user_format(u64 lstat, 6108c2ecf20Sopenharmony_ci struct cgx_link_user_info *linfo, 6118c2ecf20Sopenharmony_ci struct cgx *cgx, u8 lmac_id) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci char *lmac_string; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); 6168c2ecf20Sopenharmony_ci linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); 6178c2ecf20Sopenharmony_ci linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; 6188c2ecf20Sopenharmony_ci linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); 6198c2ecf20Sopenharmony_ci lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; 6208c2ecf20Sopenharmony_ci strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/* Hardware event handlers */ 6248c2ecf20Sopenharmony_cistatic inline void cgx_link_change_handler(u64 lstat, 6258c2ecf20Sopenharmony_ci struct lmac *lmac) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct cgx_link_user_info *linfo; 6288c2ecf20Sopenharmony_ci struct cgx *cgx = lmac->cgx; 6298c2ecf20Sopenharmony_ci struct cgx_link_event event; 6308c2ecf20Sopenharmony_ci struct device *dev; 6318c2ecf20Sopenharmony_ci int err_type; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci dev = &cgx->pdev->dev; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci link_status_user_format(lstat, &event.link_uinfo, cgx, lmac->lmac_id); 6368c2ecf20Sopenharmony_ci err_type = FIELD_GET(RESP_LINKSTAT_ERRTYPE, lstat); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci event.cgx_id = cgx->cgx_id; 6398c2ecf20Sopenharmony_ci event.lmac_id = lmac->lmac_id; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* update the local copy of link status */ 6428c2ecf20Sopenharmony_ci lmac->link_info = event.link_uinfo; 6438c2ecf20Sopenharmony_ci linfo = &lmac->link_info; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Ensure callback doesn't get unregistered until we finish it */ 6468c2ecf20Sopenharmony_ci spin_lock(&lmac->event_cb_lock); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (!lmac->event_cb.notify_link_chg) { 6498c2ecf20Sopenharmony_ci dev_dbg(dev, "cgx port %d:%d Link change handler null", 6508c2ecf20Sopenharmony_ci cgx->cgx_id, lmac->lmac_id); 6518c2ecf20Sopenharmony_ci if (err_type != CGX_ERR_NONE) { 6528c2ecf20Sopenharmony_ci dev_err(dev, "cgx port %d:%d Link error %d\n", 6538c2ecf20Sopenharmony_ci cgx->cgx_id, lmac->lmac_id, err_type); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n", 6568c2ecf20Sopenharmony_ci cgx->cgx_id, lmac->lmac_id, 6578c2ecf20Sopenharmony_ci linfo->link_up ? "UP" : "DOWN", linfo->speed); 6588c2ecf20Sopenharmony_ci goto err; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data)) 6628c2ecf20Sopenharmony_ci dev_err(dev, "event notification failure\n"); 6638c2ecf20Sopenharmony_cierr: 6648c2ecf20Sopenharmony_ci spin_unlock(&lmac->event_cb_lock); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic inline bool cgx_cmdresp_is_linkevent(u64 event) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci u8 id; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci id = FIELD_GET(EVTREG_ID, event); 6728c2ecf20Sopenharmony_ci if (id == CGX_CMD_LINK_BRING_UP || 6738c2ecf20Sopenharmony_ci id == CGX_CMD_LINK_BRING_DOWN) 6748c2ecf20Sopenharmony_ci return true; 6758c2ecf20Sopenharmony_ci else 6768c2ecf20Sopenharmony_ci return false; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic inline bool cgx_event_is_linkevent(u64 event) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci if (FIELD_GET(EVTREG_ID, event) == CGX_EVT_LINK_CHANGE) 6828c2ecf20Sopenharmony_ci return true; 6838c2ecf20Sopenharmony_ci else 6848c2ecf20Sopenharmony_ci return false; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic irqreturn_t cgx_fwi_event_handler(int irq, void *data) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct lmac *lmac = data; 6908c2ecf20Sopenharmony_ci struct cgx *cgx; 6918c2ecf20Sopenharmony_ci u64 event; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci cgx = lmac->cgx; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci event = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (!FIELD_GET(EVTREG_ACK, event)) 6988c2ecf20Sopenharmony_ci return IRQ_NONE; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci switch (FIELD_GET(EVTREG_EVT_TYPE, event)) { 7018c2ecf20Sopenharmony_ci case CGX_EVT_CMD_RESP: 7028c2ecf20Sopenharmony_ci /* Copy the response. Since only one command is active at a 7038c2ecf20Sopenharmony_ci * time, there is no way a response can get overwritten 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci lmac->resp = event; 7068c2ecf20Sopenharmony_ci /* Ensure response is updated before thread context starts */ 7078c2ecf20Sopenharmony_ci smp_wmb(); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* There wont be separate events for link change initiated from 7108c2ecf20Sopenharmony_ci * software; Hence report the command responses as events 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci if (cgx_cmdresp_is_linkevent(event)) 7138c2ecf20Sopenharmony_ci cgx_link_change_handler(event, lmac); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Release thread waiting for completion */ 7168c2ecf20Sopenharmony_ci lmac->cmd_pend = false; 7178c2ecf20Sopenharmony_ci wake_up_interruptible(&lmac->wq_cmd_cmplt); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case CGX_EVT_ASYNC: 7208c2ecf20Sopenharmony_ci if (cgx_event_is_linkevent(event)) 7218c2ecf20Sopenharmony_ci cgx_link_change_handler(event, lmac); 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Any new event or command response will be posted by firmware 7268c2ecf20Sopenharmony_ci * only after the current status is acked. 7278c2ecf20Sopenharmony_ci * Ack the interrupt register as well. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci cgx_write(lmac->cgx, lmac->lmac_id, CGX_EVENT_REG, 0); 7308c2ecf20Sopenharmony_ci cgx_write(lmac->cgx, lmac->lmac_id, CGXX_CMRX_INT, FW_CGX_INT); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/* APIs for PHY management using CGX firmware interface */ 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci/* callback registration for hardware events like link change */ 7388c2ecf20Sopenharmony_ciint cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 7418c2ecf20Sopenharmony_ci struct lmac *lmac; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 7448c2ecf20Sopenharmony_ci if (!lmac) 7458c2ecf20Sopenharmony_ci return -ENODEV; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci lmac->event_cb = *cb; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ciint cgx_lmac_evh_unregister(void *cgxd, int lmac_id) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct lmac *lmac; 7558c2ecf20Sopenharmony_ci unsigned long flags; 7568c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci lmac = lmac_pdata(lmac_id, cgx); 7598c2ecf20Sopenharmony_ci if (!lmac) 7608c2ecf20Sopenharmony_ci return -ENODEV; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci spin_lock_irqsave(&lmac->event_cb_lock, flags); 7638c2ecf20Sopenharmony_ci lmac->event_cb.notify_link_chg = NULL; 7648c2ecf20Sopenharmony_ci lmac->event_cb.data = NULL; 7658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lmac->event_cb_lock, flags); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ciint cgx_get_fwdata_base(u64 *base) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci u64 req = 0, resp; 7738c2ecf20Sopenharmony_ci struct cgx *cgx; 7748c2ecf20Sopenharmony_ci int err; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci cgx = list_first_entry_or_null(&cgx_list, struct cgx, cgx_list); 7778c2ecf20Sopenharmony_ci if (!cgx) 7788c2ecf20Sopenharmony_ci return -ENXIO; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req); 7818c2ecf20Sopenharmony_ci err = cgx_fwi_cmd_generic(req, &resp, cgx, 0); 7828c2ecf20Sopenharmony_ci if (!err) 7838c2ecf20Sopenharmony_ci *base = FIELD_GET(RESP_FWD_BASE, resp); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return err; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci u64 req = 0; 7918c2ecf20Sopenharmony_ci u64 resp; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (enable) 7948c2ecf20Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req); 7958c2ecf20Sopenharmony_ci else 7968c2ecf20Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci u64 req = 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req); 8068c2ecf20Sopenharmony_ci return cgx_fwi_cmd_generic(req, resp, cgx, 0); 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistatic int cgx_lmac_verify_fwi_version(struct cgx *cgx) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct device *dev = &cgx->pdev->dev; 8128c2ecf20Sopenharmony_ci int major_ver, minor_ver; 8138c2ecf20Sopenharmony_ci u64 resp; 8148c2ecf20Sopenharmony_ci int err; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!cgx->lmac_count) 8178c2ecf20Sopenharmony_ci return 0; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci err = cgx_fwi_read_version(&resp, cgx); 8208c2ecf20Sopenharmony_ci if (err) 8218c2ecf20Sopenharmony_ci return err; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci major_ver = FIELD_GET(RESP_MAJOR_VER, resp); 8248c2ecf20Sopenharmony_ci minor_ver = FIELD_GET(RESP_MINOR_VER, resp); 8258c2ecf20Sopenharmony_ci dev_dbg(dev, "Firmware command interface version = %d.%d\n", 8268c2ecf20Sopenharmony_ci major_ver, minor_ver); 8278c2ecf20Sopenharmony_ci if (major_ver != CGX_FIRMWARE_MAJOR_VER) 8288c2ecf20Sopenharmony_ci return -EIO; 8298c2ecf20Sopenharmony_ci else 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void cgx_lmac_linkup_work(struct work_struct *work) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work); 8368c2ecf20Sopenharmony_ci struct device *dev = &cgx->pdev->dev; 8378c2ecf20Sopenharmony_ci int i, err; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* Do Link up for all the lmacs */ 8408c2ecf20Sopenharmony_ci for (i = 0; i < cgx->lmac_count; i++) { 8418c2ecf20Sopenharmony_ci err = cgx_fwi_link_change(cgx, i, true); 8428c2ecf20Sopenharmony_ci if (err) 8438c2ecf20Sopenharmony_ci dev_info(dev, "cgx port %d:%d Link up command failed\n", 8448c2ecf20Sopenharmony_ci cgx->cgx_id, i); 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ciint cgx_lmac_linkup_start(void *cgxd) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct cgx *cgx = cgxd; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (!cgx) 8538c2ecf20Sopenharmony_ci return -ENODEV; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic int cgx_lmac_init(struct cgx *cgx) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct lmac *lmac; 8638c2ecf20Sopenharmony_ci int i, err; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0x7; 8668c2ecf20Sopenharmony_ci if (cgx->lmac_count > MAX_LMAC_PER_CGX) 8678c2ecf20Sopenharmony_ci cgx->lmac_count = MAX_LMAC_PER_CGX; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci for (i = 0; i < cgx->lmac_count; i++) { 8708c2ecf20Sopenharmony_ci lmac = kcalloc(1, sizeof(struct lmac), GFP_KERNEL); 8718c2ecf20Sopenharmony_ci if (!lmac) 8728c2ecf20Sopenharmony_ci return -ENOMEM; 8738c2ecf20Sopenharmony_ci lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL); 8748c2ecf20Sopenharmony_ci if (!lmac->name) { 8758c2ecf20Sopenharmony_ci err = -ENOMEM; 8768c2ecf20Sopenharmony_ci goto err_lmac_free; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i); 8798c2ecf20Sopenharmony_ci lmac->lmac_id = i; 8808c2ecf20Sopenharmony_ci lmac->cgx = cgx; 8818c2ecf20Sopenharmony_ci init_waitqueue_head(&lmac->wq_cmd_cmplt); 8828c2ecf20Sopenharmony_ci mutex_init(&lmac->cmd_lock); 8838c2ecf20Sopenharmony_ci spin_lock_init(&lmac->event_cb_lock); 8848c2ecf20Sopenharmony_ci err = request_irq(pci_irq_vector(cgx->pdev, 8858c2ecf20Sopenharmony_ci CGX_LMAC_FWI + i * 9), 8868c2ecf20Sopenharmony_ci cgx_fwi_event_handler, 0, lmac->name, lmac); 8878c2ecf20Sopenharmony_ci if (err) 8888c2ecf20Sopenharmony_ci goto err_irq; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* Enable interrupt */ 8918c2ecf20Sopenharmony_ci cgx_write(cgx, lmac->lmac_id, CGXX_CMRX_INT_ENA_W1S, 8928c2ecf20Sopenharmony_ci FW_CGX_INT); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Add reference */ 8958c2ecf20Sopenharmony_ci cgx->lmac_idmap[i] = lmac; 8968c2ecf20Sopenharmony_ci cgx_lmac_pause_frm_config(cgx, i, true); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return cgx_lmac_verify_fwi_version(cgx); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cierr_irq: 9028c2ecf20Sopenharmony_ci kfree(lmac->name); 9038c2ecf20Sopenharmony_cierr_lmac_free: 9048c2ecf20Sopenharmony_ci kfree(lmac); 9058c2ecf20Sopenharmony_ci return err; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic int cgx_lmac_exit(struct cgx *cgx) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci struct lmac *lmac; 9118c2ecf20Sopenharmony_ci int i; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (cgx->cgx_cmd_workq) { 9148c2ecf20Sopenharmony_ci flush_workqueue(cgx->cgx_cmd_workq); 9158c2ecf20Sopenharmony_ci destroy_workqueue(cgx->cgx_cmd_workq); 9168c2ecf20Sopenharmony_ci cgx->cgx_cmd_workq = NULL; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* Free all lmac related resources */ 9208c2ecf20Sopenharmony_ci for (i = 0; i < cgx->lmac_count; i++) { 9218c2ecf20Sopenharmony_ci cgx_lmac_pause_frm_config(cgx, i, false); 9228c2ecf20Sopenharmony_ci lmac = cgx->lmac_idmap[i]; 9238c2ecf20Sopenharmony_ci if (!lmac) 9248c2ecf20Sopenharmony_ci continue; 9258c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(cgx->pdev, CGX_LMAC_FWI + i * 9), lmac); 9268c2ecf20Sopenharmony_ci kfree(lmac->name); 9278c2ecf20Sopenharmony_ci kfree(lmac); 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 9368c2ecf20Sopenharmony_ci struct cgx *cgx; 9378c2ecf20Sopenharmony_ci int err, nvec; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci cgx = devm_kzalloc(dev, sizeof(*cgx), GFP_KERNEL); 9408c2ecf20Sopenharmony_ci if (!cgx) 9418c2ecf20Sopenharmony_ci return -ENOMEM; 9428c2ecf20Sopenharmony_ci cgx->pdev = pdev; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, cgx); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 9478c2ecf20Sopenharmony_ci if (err) { 9488c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable PCI device\n"); 9498c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 9508c2ecf20Sopenharmony_ci return err; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 9548c2ecf20Sopenharmony_ci if (err) { 9558c2ecf20Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 9568c2ecf20Sopenharmony_ci goto err_disable_device; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* MAP configuration registers */ 9608c2ecf20Sopenharmony_ci cgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); 9618c2ecf20Sopenharmony_ci if (!cgx->reg_base) { 9628c2ecf20Sopenharmony_ci dev_err(dev, "CGX: Cannot map CSR memory space, aborting\n"); 9638c2ecf20Sopenharmony_ci err = -ENOMEM; 9648c2ecf20Sopenharmony_ci goto err_release_regions; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci nvec = CGX_NVEC; 9688c2ecf20Sopenharmony_ci err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX); 9698c2ecf20Sopenharmony_ci if (err < 0 || err != nvec) { 9708c2ecf20Sopenharmony_ci dev_err(dev, "Request for %d msix vectors failed, err %d\n", 9718c2ecf20Sopenharmony_ci nvec, err); 9728c2ecf20Sopenharmony_ci goto err_release_regions; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24) 9768c2ecf20Sopenharmony_ci & CGX_ID_MASK; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* init wq for processing linkup requests */ 9798c2ecf20Sopenharmony_ci INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work); 9808c2ecf20Sopenharmony_ci cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0); 9818c2ecf20Sopenharmony_ci if (!cgx->cgx_cmd_workq) { 9828c2ecf20Sopenharmony_ci dev_err(dev, "alloc workqueue failed for cgx cmd"); 9838c2ecf20Sopenharmony_ci err = -ENOMEM; 9848c2ecf20Sopenharmony_ci goto err_free_irq_vectors; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci list_add(&cgx->cgx_list, &cgx_list); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci cgx_link_usertable_init(); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci err = cgx_lmac_init(cgx); 9928c2ecf20Sopenharmony_ci if (err) 9938c2ecf20Sopenharmony_ci goto err_release_lmac; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci return 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cierr_release_lmac: 9988c2ecf20Sopenharmony_ci cgx_lmac_exit(cgx); 9998c2ecf20Sopenharmony_ci list_del(&cgx->cgx_list); 10008c2ecf20Sopenharmony_cierr_free_irq_vectors: 10018c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 10028c2ecf20Sopenharmony_cierr_release_regions: 10038c2ecf20Sopenharmony_ci pci_release_regions(pdev); 10048c2ecf20Sopenharmony_cierr_disable_device: 10058c2ecf20Sopenharmony_ci pci_disable_device(pdev); 10068c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 10078c2ecf20Sopenharmony_ci return err; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic void cgx_remove(struct pci_dev *pdev) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct cgx *cgx = pci_get_drvdata(pdev); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci cgx_lmac_exit(cgx); 10158c2ecf20Sopenharmony_ci list_del(&cgx->cgx_list); 10168c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 10178c2ecf20Sopenharmony_ci pci_release_regions(pdev); 10188c2ecf20Sopenharmony_ci pci_disable_device(pdev); 10198c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistruct pci_driver cgx_driver = { 10238c2ecf20Sopenharmony_ci .name = DRV_NAME, 10248c2ecf20Sopenharmony_ci .id_table = cgx_id_table, 10258c2ecf20Sopenharmony_ci .probe = cgx_probe, 10268c2ecf20Sopenharmony_ci .remove = cgx_remove, 10278c2ecf20Sopenharmony_ci}; 1028