xref: /linux/drivers/fwctl/bnxt/main.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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