1*e782efb8SPavan Chebbi // SPDX-License-Identifier: GPL-2.0 2*e782efb8SPavan Chebbi /* 3*e782efb8SPavan Chebbi * Copyright (c) 2026, Broadcom Corporation 4*e782efb8SPavan Chebbi */ 5*e782efb8SPavan Chebbi 6*e782efb8SPavan Chebbi #include <linux/auxiliary_bus.h> 7*e782efb8SPavan Chebbi #include <linux/slab.h> 8*e782efb8SPavan Chebbi #include <linux/pci.h> 9*e782efb8SPavan Chebbi #include <linux/fwctl.h> 10*e782efb8SPavan Chebbi #include <linux/bnxt/hsi.h> 11*e782efb8SPavan Chebbi #include <linux/bnxt/ulp.h> 12*e782efb8SPavan Chebbi #include <uapi/fwctl/fwctl.h> 13*e782efb8SPavan Chebbi #include <uapi/fwctl/bnxt.h> 14*e782efb8SPavan Chebbi 15*e782efb8SPavan Chebbi struct bnxtctl_uctx { 16*e782efb8SPavan Chebbi struct fwctl_uctx uctx; 17*e782efb8SPavan Chebbi u32 uctx_caps; 18*e782efb8SPavan Chebbi }; 19*e782efb8SPavan Chebbi 20*e782efb8SPavan Chebbi struct bnxtctl_dev { 21*e782efb8SPavan Chebbi struct fwctl_device fwctl; 22*e782efb8SPavan Chebbi struct bnxt_aux_priv *aux_priv; 23*e782efb8SPavan Chebbi }; 24*e782efb8SPavan Chebbi 25*e782efb8SPavan Chebbi DEFINE_FREE(bnxtctl, struct bnxtctl_dev *, if (_T) fwctl_put(&_T->fwctl)) 26*e782efb8SPavan Chebbi 27*e782efb8SPavan Chebbi static int bnxtctl_open_uctx(struct fwctl_uctx *uctx) 28*e782efb8SPavan Chebbi { 29*e782efb8SPavan Chebbi struct bnxtctl_uctx *bnxtctl_uctx = 30*e782efb8SPavan Chebbi container_of(uctx, struct bnxtctl_uctx, uctx); 31*e782efb8SPavan Chebbi 32*e782efb8SPavan Chebbi bnxtctl_uctx->uctx_caps = BIT(FWCTL_BNXT_INLINE_COMMANDS) | 33*e782efb8SPavan Chebbi BIT(FWCTL_BNXT_QUERY_COMMANDS) | 34*e782efb8SPavan Chebbi BIT(FWCTL_BNXT_SEND_COMMANDS); 35*e782efb8SPavan Chebbi return 0; 36*e782efb8SPavan Chebbi } 37*e782efb8SPavan Chebbi 38*e782efb8SPavan Chebbi static void bnxtctl_close_uctx(struct fwctl_uctx *uctx) 39*e782efb8SPavan Chebbi { 40*e782efb8SPavan Chebbi } 41*e782efb8SPavan Chebbi 42*e782efb8SPavan Chebbi static void *bnxtctl_info(struct fwctl_uctx *uctx, size_t *length) 43*e782efb8SPavan Chebbi { 44*e782efb8SPavan Chebbi struct bnxtctl_uctx *bnxtctl_uctx = 45*e782efb8SPavan Chebbi container_of(uctx, struct bnxtctl_uctx, uctx); 46*e782efb8SPavan Chebbi struct fwctl_info_bnxt *info; 47*e782efb8SPavan Chebbi 48*e782efb8SPavan Chebbi info = kzalloc_obj(*info); 49*e782efb8SPavan Chebbi if (!info) 50*e782efb8SPavan Chebbi return ERR_PTR(-ENOMEM); 51*e782efb8SPavan Chebbi 52*e782efb8SPavan Chebbi info->uctx_caps = bnxtctl_uctx->uctx_caps; 53*e782efb8SPavan Chebbi 54*e782efb8SPavan Chebbi *length = sizeof(*info); 55*e782efb8SPavan Chebbi return info; 56*e782efb8SPavan Chebbi } 57*e782efb8SPavan Chebbi 58*e782efb8SPavan Chebbi /* Caller must hold edev->en_dev_lock */ 59*e782efb8SPavan Chebbi static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev, 60*e782efb8SPavan Chebbi struct bnxt_fw_msg *hwrm_in, 61*e782efb8SPavan Chebbi enum fwctl_rpc_scope scope) 62*e782efb8SPavan Chebbi { 63*e782efb8SPavan Chebbi struct input *req = (struct input *)hwrm_in->msg; 64*e782efb8SPavan Chebbi 65*e782efb8SPavan Chebbi lockdep_assert_held(&edev->en_dev_lock); 66*e782efb8SPavan Chebbi if (edev->flags & BNXT_EN_FLAG_ULP_STOPPED) 67*e782efb8SPavan Chebbi return false; 68*e782efb8SPavan Chebbi 69*e782efb8SPavan Chebbi switch (le16_to_cpu(req->req_type)) { 70*e782efb8SPavan Chebbi case HWRM_FUNC_RESET: 71*e782efb8SPavan Chebbi case HWRM_PORT_CLR_STATS: 72*e782efb8SPavan Chebbi case HWRM_FW_RESET: 73*e782efb8SPavan Chebbi case HWRM_FW_SYNC: 74*e782efb8SPavan Chebbi case HWRM_FW_SET_TIME: 75*e782efb8SPavan Chebbi case HWRM_DBG_LOG_BUFFER_FLUSH: 76*e782efb8SPavan Chebbi case HWRM_DBG_ERASE_NVM: 77*e782efb8SPavan Chebbi case HWRM_DBG_CFG: 78*e782efb8SPavan Chebbi case HWRM_NVM_DEFRAG: 79*e782efb8SPavan Chebbi case HWRM_NVM_FACTORY_DEFAULTS: 80*e782efb8SPavan Chebbi case HWRM_NVM_FLUSH: 81*e782efb8SPavan Chebbi case HWRM_NVM_VERIFY_UPDATE: 82*e782efb8SPavan Chebbi case HWRM_NVM_ERASE_DIR_ENTRY: 83*e782efb8SPavan Chebbi case HWRM_NVM_MOD_DIR_ENTRY: 84*e782efb8SPavan Chebbi case HWRM_NVM_FIND_DIR_ENTRY: 85*e782efb8SPavan Chebbi return scope >= FWCTL_RPC_CONFIGURATION; 86*e782efb8SPavan Chebbi 87*e782efb8SPavan Chebbi case HWRM_VER_GET: 88*e782efb8SPavan Chebbi case HWRM_ERROR_RECOVERY_QCFG: 89*e782efb8SPavan Chebbi case HWRM_FUNC_QCAPS: 90*e782efb8SPavan Chebbi case HWRM_FUNC_QCFG: 91*e782efb8SPavan Chebbi case HWRM_FUNC_QSTATS: 92*e782efb8SPavan Chebbi case HWRM_PORT_PHY_QCFG: 93*e782efb8SPavan Chebbi case HWRM_PORT_MAC_QCFG: 94*e782efb8SPavan Chebbi case HWRM_PORT_PHY_QCAPS: 95*e782efb8SPavan Chebbi case HWRM_PORT_PHY_I2C_READ: 96*e782efb8SPavan Chebbi case HWRM_PORT_PHY_MDIO_READ: 97*e782efb8SPavan Chebbi case HWRM_QUEUE_PRI2COS_QCFG: 98*e782efb8SPavan Chebbi case HWRM_QUEUE_COS2BW_QCFG: 99*e782efb8SPavan Chebbi case HWRM_VNIC_RSS_QCFG: 100*e782efb8SPavan Chebbi case HWRM_QUEUE_GLOBAL_QCFG: 101*e782efb8SPavan Chebbi case HWRM_QUEUE_ADPTV_QOS_RX_FEATURE_QCFG: 102*e782efb8SPavan Chebbi case HWRM_QUEUE_ADPTV_QOS_TX_FEATURE_QCFG: 103*e782efb8SPavan Chebbi case HWRM_QUEUE_QCAPS: 104*e782efb8SPavan Chebbi case HWRM_QUEUE_ADPTV_QOS_RX_TUNING_QCFG: 105*e782efb8SPavan Chebbi case HWRM_QUEUE_ADPTV_QOS_TX_TUNING_QCFG: 106*e782efb8SPavan Chebbi case HWRM_TUNNEL_DST_PORT_QUERY: 107*e782efb8SPavan Chebbi case HWRM_PORT_TX_FIR_QCFG: 108*e782efb8SPavan Chebbi case HWRM_FW_LIVEPATCH_QUERY: 109*e782efb8SPavan Chebbi case HWRM_FW_QSTATUS: 110*e782efb8SPavan Chebbi case HWRM_FW_HEALTH_CHECK: 111*e782efb8SPavan Chebbi case HWRM_FW_GET_TIME: 112*e782efb8SPavan Chebbi case HWRM_PORT_EP_TX_QCFG: 113*e782efb8SPavan Chebbi case HWRM_PORT_QCFG: 114*e782efb8SPavan Chebbi case HWRM_PORT_MAC_QCAPS: 115*e782efb8SPavan Chebbi case HWRM_TEMP_MONITOR_QUERY: 116*e782efb8SPavan Chebbi case HWRM_REG_POWER_QUERY: 117*e782efb8SPavan Chebbi case HWRM_CORE_FREQUENCY_QUERY: 118*e782efb8SPavan Chebbi case HWRM_CFA_REDIRECT_QUERY_TUNNEL_TYPE: 119*e782efb8SPavan Chebbi case HWRM_CFA_ADV_FLOW_MGNT_QCAPS: 120*e782efb8SPavan Chebbi case HWRM_FUNC_RESOURCE_QCAPS: 121*e782efb8SPavan Chebbi case HWRM_FUNC_BACKING_STORE_QCAPS: 122*e782efb8SPavan Chebbi case HWRM_FUNC_BACKING_STORE_QCFG: 123*e782efb8SPavan Chebbi case HWRM_FUNC_QSTATS_EXT: 124*e782efb8SPavan Chebbi case HWRM_FUNC_PTP_PIN_QCFG: 125*e782efb8SPavan Chebbi case HWRM_FUNC_PTP_EXT_QCFG: 126*e782efb8SPavan Chebbi case HWRM_FUNC_BACKING_STORE_QCFG_V2: 127*e782efb8SPavan Chebbi case HWRM_FUNC_BACKING_STORE_QCAPS_V2: 128*e782efb8SPavan Chebbi case HWRM_FUNC_SYNCE_QCFG: 129*e782efb8SPavan Chebbi case HWRM_FUNC_TTX_PACING_RATE_PROF_QUERY: 130*e782efb8SPavan Chebbi case HWRM_PORT_PHY_FDRSTAT: 131*e782efb8SPavan Chebbi case HWRM_DBG_RING_INFO_GET: 132*e782efb8SPavan Chebbi case HWRM_DBG_QCAPS: 133*e782efb8SPavan Chebbi case HWRM_DBG_QCFG: 134*e782efb8SPavan Chebbi case HWRM_DBG_USEQ_FLUSH: 135*e782efb8SPavan Chebbi case HWRM_DBG_USEQ_QCAPS: 136*e782efb8SPavan Chebbi case HWRM_DBG_SIM_CABLE_STATE: 137*e782efb8SPavan Chebbi case HWRM_DBG_TOKEN_QUERY_AUTH_IDS: 138*e782efb8SPavan Chebbi case HWRM_NVM_GET_DEV_INFO: 139*e782efb8SPavan Chebbi case HWRM_NVM_GET_DIR_INFO: 140*e782efb8SPavan Chebbi case HWRM_SELFTEST_QLIST: 141*e782efb8SPavan Chebbi return scope >= FWCTL_RPC_DEBUG_READ_ONLY; 142*e782efb8SPavan Chebbi 143*e782efb8SPavan Chebbi case HWRM_PORT_PHY_I2C_WRITE: 144*e782efb8SPavan Chebbi case HWRM_PORT_PHY_MDIO_WRITE: 145*e782efb8SPavan Chebbi return scope >= FWCTL_RPC_DEBUG_WRITE; 146*e782efb8SPavan Chebbi 147*e782efb8SPavan Chebbi default: 148*e782efb8SPavan Chebbi return false; 149*e782efb8SPavan Chebbi } 150*e782efb8SPavan Chebbi } 151*e782efb8SPavan Chebbi 152*e782efb8SPavan Chebbi #define BNXTCTL_HWRM_CMD_TIMEOUT_DFLT 500 /* ms */ 153*e782efb8SPavan Chebbi #define BNXTCTL_HWRM_CMD_TIMEOUT_MEDM 2000 /* ms */ 154*e782efb8SPavan Chebbi #define BNXTCTL_HWRM_CMD_TIMEOUT_LONG 60000 /* ms */ 155*e782efb8SPavan Chebbi 156*e782efb8SPavan Chebbi static unsigned int bnxtctl_get_timeout(struct input *req) 157*e782efb8SPavan Chebbi { 158*e782efb8SPavan Chebbi switch (le16_to_cpu(req->req_type)) { 159*e782efb8SPavan Chebbi case HWRM_NVM_DEFRAG: 160*e782efb8SPavan Chebbi case HWRM_NVM_FACTORY_DEFAULTS: 161*e782efb8SPavan Chebbi case HWRM_NVM_FLUSH: 162*e782efb8SPavan Chebbi case HWRM_NVM_VERIFY_UPDATE: 163*e782efb8SPavan Chebbi case HWRM_NVM_ERASE_DIR_ENTRY: 164*e782efb8SPavan Chebbi case HWRM_NVM_MOD_DIR_ENTRY: 165*e782efb8SPavan Chebbi return BNXTCTL_HWRM_CMD_TIMEOUT_LONG; 166*e782efb8SPavan Chebbi case HWRM_FUNC_RESET: 167*e782efb8SPavan Chebbi return BNXTCTL_HWRM_CMD_TIMEOUT_MEDM; 168*e782efb8SPavan Chebbi default: 169*e782efb8SPavan Chebbi return BNXTCTL_HWRM_CMD_TIMEOUT_DFLT; 170*e782efb8SPavan Chebbi } 171*e782efb8SPavan Chebbi } 172*e782efb8SPavan Chebbi 173*e782efb8SPavan Chebbi static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx, 174*e782efb8SPavan Chebbi enum fwctl_rpc_scope scope, 175*e782efb8SPavan Chebbi void *in, size_t in_len, size_t *out_len) 176*e782efb8SPavan Chebbi { 177*e782efb8SPavan Chebbi struct bnxtctl_dev *bnxtctl = 178*e782efb8SPavan Chebbi container_of(uctx->fwctl, struct bnxtctl_dev, fwctl); 179*e782efb8SPavan Chebbi struct bnxt_en_dev *edev = bnxtctl->aux_priv->edev; 180*e782efb8SPavan Chebbi struct bnxt_fw_msg rpc_in = {0}; 181*e782efb8SPavan Chebbi int rc; 182*e782efb8SPavan Chebbi 183*e782efb8SPavan Chebbi if (in_len < sizeof(struct input) || in_len > HWRM_MAX_REQ_LEN) 184*e782efb8SPavan Chebbi return ERR_PTR(-EINVAL); 185*e782efb8SPavan Chebbi 186*e782efb8SPavan Chebbi if (*out_len < sizeof(struct output)) 187*e782efb8SPavan Chebbi return ERR_PTR(-EINVAL); 188*e782efb8SPavan Chebbi 189*e782efb8SPavan Chebbi rpc_in.msg = in; 190*e782efb8SPavan Chebbi rpc_in.msg_len = in_len; 191*e782efb8SPavan Chebbi rpc_in.resp = kzalloc(*out_len, GFP_KERNEL); 192*e782efb8SPavan Chebbi if (!rpc_in.resp) 193*e782efb8SPavan Chebbi return ERR_PTR(-ENOMEM); 194*e782efb8SPavan Chebbi 195*e782efb8SPavan Chebbi rpc_in.resp_max_len = *out_len; 196*e782efb8SPavan Chebbi rpc_in.timeout = bnxtctl_get_timeout(in); 197*e782efb8SPavan Chebbi 198*e782efb8SPavan Chebbi guard(mutex)(&edev->en_dev_lock); 199*e782efb8SPavan Chebbi 200*e782efb8SPavan Chebbi if (!bnxtctl_validate_rpc(edev, &rpc_in, scope)) { 201*e782efb8SPavan Chebbi kfree(rpc_in.resp); 202*e782efb8SPavan Chebbi return ERR_PTR(-EPERM); 203*e782efb8SPavan Chebbi } 204*e782efb8SPavan Chebbi 205*e782efb8SPavan Chebbi rc = bnxt_send_msg(edev, &rpc_in); 206*e782efb8SPavan Chebbi if (rc) { 207*e782efb8SPavan Chebbi struct output *resp = rpc_in.resp; 208*e782efb8SPavan Chebbi 209*e782efb8SPavan Chebbi /* Copy the response to user always, as it contains 210*e782efb8SPavan Chebbi * detailed status of the command failure 211*e782efb8SPavan Chebbi */ 212*e782efb8SPavan Chebbi if (!resp->error_code) 213*e782efb8SPavan Chebbi /* bnxt_send_msg() returned much before FW 214*e782efb8SPavan Chebbi * received the command. 215*e782efb8SPavan Chebbi */ 216*e782efb8SPavan Chebbi resp->error_code = cpu_to_le16(rc); 217*e782efb8SPavan Chebbi } 218*e782efb8SPavan Chebbi 219*e782efb8SPavan Chebbi return rpc_in.resp; 220*e782efb8SPavan Chebbi } 221*e782efb8SPavan Chebbi 222*e782efb8SPavan Chebbi static const struct fwctl_ops bnxtctl_ops = { 223*e782efb8SPavan Chebbi .device_type = FWCTL_DEVICE_TYPE_BNXT, 224*e782efb8SPavan Chebbi .uctx_size = sizeof(struct bnxtctl_uctx), 225*e782efb8SPavan Chebbi .open_uctx = bnxtctl_open_uctx, 226*e782efb8SPavan Chebbi .close_uctx = bnxtctl_close_uctx, 227*e782efb8SPavan Chebbi .info = bnxtctl_info, 228*e782efb8SPavan Chebbi .fw_rpc = bnxtctl_fw_rpc, 229*e782efb8SPavan Chebbi }; 230*e782efb8SPavan Chebbi 231*e782efb8SPavan Chebbi static int bnxtctl_probe(struct auxiliary_device *adev, 232*e782efb8SPavan Chebbi const struct auxiliary_device_id *id) 233*e782efb8SPavan Chebbi { 234*e782efb8SPavan Chebbi struct bnxt_aux_priv *aux_priv = 235*e782efb8SPavan Chebbi container_of(adev, struct bnxt_aux_priv, aux_dev); 236*e782efb8SPavan Chebbi struct bnxtctl_dev *bnxtctl __free(bnxtctl) = 237*e782efb8SPavan Chebbi fwctl_alloc_device(&aux_priv->edev->pdev->dev, &bnxtctl_ops, 238*e782efb8SPavan Chebbi struct bnxtctl_dev, fwctl); 239*e782efb8SPavan Chebbi int rc; 240*e782efb8SPavan Chebbi 241*e782efb8SPavan Chebbi if (!bnxtctl) 242*e782efb8SPavan Chebbi return -ENOMEM; 243*e782efb8SPavan Chebbi 244*e782efb8SPavan Chebbi bnxtctl->aux_priv = aux_priv; 245*e782efb8SPavan Chebbi 246*e782efb8SPavan Chebbi rc = fwctl_register(&bnxtctl->fwctl); 247*e782efb8SPavan Chebbi if (rc) 248*e782efb8SPavan Chebbi return rc; 249*e782efb8SPavan Chebbi 250*e782efb8SPavan Chebbi auxiliary_set_drvdata(adev, no_free_ptr(bnxtctl)); 251*e782efb8SPavan Chebbi return 0; 252*e782efb8SPavan Chebbi } 253*e782efb8SPavan Chebbi 254*e782efb8SPavan Chebbi static void bnxtctl_remove(struct auxiliary_device *adev) 255*e782efb8SPavan Chebbi { 256*e782efb8SPavan Chebbi struct bnxtctl_dev *ctldev = auxiliary_get_drvdata(adev); 257*e782efb8SPavan Chebbi 258*e782efb8SPavan Chebbi fwctl_unregister(&ctldev->fwctl); 259*e782efb8SPavan Chebbi fwctl_put(&ctldev->fwctl); 260*e782efb8SPavan Chebbi } 261*e782efb8SPavan Chebbi 262*e782efb8SPavan Chebbi static const struct auxiliary_device_id bnxtctl_id_table[] = { 263*e782efb8SPavan Chebbi { .name = "bnxt_en.fwctl", }, 264*e782efb8SPavan Chebbi {} 265*e782efb8SPavan Chebbi }; 266*e782efb8SPavan Chebbi MODULE_DEVICE_TABLE(auxiliary, bnxtctl_id_table); 267*e782efb8SPavan Chebbi 268*e782efb8SPavan Chebbi static struct auxiliary_driver bnxtctl_driver = { 269*e782efb8SPavan Chebbi .name = "bnxt_fwctl", 270*e782efb8SPavan Chebbi .probe = bnxtctl_probe, 271*e782efb8SPavan Chebbi .remove = bnxtctl_remove, 272*e782efb8SPavan Chebbi .id_table = bnxtctl_id_table, 273*e782efb8SPavan Chebbi }; 274*e782efb8SPavan Chebbi 275*e782efb8SPavan Chebbi module_auxiliary_driver(bnxtctl_driver); 276*e782efb8SPavan Chebbi 277*e782efb8SPavan Chebbi MODULE_IMPORT_NS("FWCTL"); 278*e782efb8SPavan Chebbi MODULE_DESCRIPTION("BNXT fwctl driver"); 279*e782efb8SPavan Chebbi MODULE_AUTHOR("Pavan Chebbi <pavan.chebbi@broadcom.com>"); 280*e782efb8SPavan Chebbi MODULE_AUTHOR("Andy Gospodarek <gospo@broadcom.com>"); 281*e782efb8SPavan Chebbi MODULE_LICENSE("GPL"); 282