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