1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Marvell Octeon EP (EndPoint) Ethernet Driver |
3 | * |
4 | * Copyright (C) 2020 Marvell. |
5 | * |
6 | */ |
7 | #include <linux/string.h> |
8 | #include <linux/types.h> |
9 | #include <linux/etherdevice.h> |
10 | #include <linux/pci.h> |
11 | #include <linux/wait.h> |
12 | |
13 | #include "octep_config.h" |
14 | #include "octep_main.h" |
15 | #include "octep_ctrl_net.h" |
16 | #include "octep_pfvf_mbox.h" |
17 | |
18 | /* Control plane version */ |
19 | #define OCTEP_CP_VERSION_CURRENT OCTEP_CP_VERSION(1, 0, 0) |
20 | |
21 | static const u32 req_hdr_sz = sizeof(union octep_ctrl_net_req_hdr); |
22 | static const u32 mtu_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_mtu); |
23 | static const u32 mac_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_mac); |
24 | static const u32 state_sz = sizeof(struct octep_ctrl_net_h2f_req_cmd_state); |
25 | static const u32 link_info_sz = sizeof(struct octep_ctrl_net_link_info); |
26 | static const u32 offloads_sz = sizeof(struct octep_ctrl_net_offloads); |
27 | static atomic_t ctrl_net_msg_id; |
28 | |
29 | /* Control plane version in which OCTEP_CTRL_NET_H2F_CMD was added */ |
30 | static const u32 octep_ctrl_net_h2f_cmd_versions[OCTEP_CTRL_NET_H2F_CMD_MAX] = { |
31 | [OCTEP_CTRL_NET_H2F_CMD_INVALID ... OCTEP_CTRL_NET_H2F_CMD_DEV_REMOVE] = |
32 | OCTEP_CP_VERSION(1, 0, 0), |
33 | [OCTEP_CTRL_NET_H2F_CMD_OFFLOADS] = OCTEP_CP_VERSION(1, 0, 1) |
34 | |
35 | }; |
36 | |
37 | /* Control plane version in which OCTEP_CTRL_NET_F2H_CMD was added */ |
38 | static const u32 octep_ctrl_net_f2h_cmd_versions[OCTEP_CTRL_NET_F2H_CMD_MAX] = { |
39 | [OCTEP_CTRL_NET_F2H_CMD_INVALID ... OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS] = |
40 | OCTEP_CP_VERSION(1, 0, 0) |
41 | }; |
42 | |
43 | static void init_send_req(struct octep_ctrl_mbox_msg *msg, void *buf, |
44 | u16 sz, int vfid) |
45 | { |
46 | msg->hdr.s.flags = OCTEP_CTRL_MBOX_MSG_HDR_FLAG_REQ; |
47 | msg->hdr.s.msg_id = atomic_inc_return(v: &ctrl_net_msg_id) & |
48 | GENMASK(sizeof(msg->hdr.s.msg_id) * BITS_PER_BYTE, 0); |
49 | msg->hdr.s.sz = req_hdr_sz + sz; |
50 | msg->sg_num = 1; |
51 | msg->sg_list[0].msg = buf; |
52 | msg->sg_list[0].sz = msg->hdr.s.sz; |
53 | if (vfid != OCTEP_CTRL_NET_INVALID_VFID) { |
54 | msg->hdr.s.is_vf = 1; |
55 | msg->hdr.s.vf_idx = vfid; |
56 | } |
57 | } |
58 | |
59 | static int octep_send_mbox_req(struct octep_device *oct, |
60 | struct octep_ctrl_net_wait_data *d, |
61 | bool wait_for_response) |
62 | { |
63 | int err, ret, cmd; |
64 | |
65 | /* check if firmware is compatible for this request */ |
66 | cmd = d->data.req.hdr.s.cmd; |
67 | if (octep_ctrl_net_h2f_cmd_versions[cmd] > oct->ctrl_mbox.max_fw_version || |
68 | octep_ctrl_net_h2f_cmd_versions[cmd] < oct->ctrl_mbox.min_fw_version) |
69 | return -EOPNOTSUPP; |
70 | |
71 | err = octep_ctrl_mbox_send(mbox: &oct->ctrl_mbox, msg: &d->msg); |
72 | if (err < 0) |
73 | return err; |
74 | |
75 | if (!wait_for_response) |
76 | return 0; |
77 | |
78 | d->done = 0; |
79 | INIT_LIST_HEAD(list: &d->list); |
80 | list_add_tail(new: &d->list, head: &oct->ctrl_req_wait_list); |
81 | ret = wait_event_interruptible_timeout(oct->ctrl_req_wait_q, |
82 | (d->done != 0), |
83 | msecs_to_jiffies(500)); |
84 | list_del(entry: &d->list); |
85 | if (ret == 0 || ret == 1) |
86 | return -EAGAIN; |
87 | |
88 | /** |
89 | * (ret == 0) cond = false && timeout, return 0 |
90 | * (ret < 0) interrupted by signal, return 0 |
91 | * (ret == 1) cond = true && timeout, return 1 |
92 | * (ret >= 1) cond = true && !timeout, return 1 |
93 | */ |
94 | |
95 | if (d->data.resp.hdr.s.reply != OCTEP_CTRL_NET_REPLY_OK) |
96 | return -EAGAIN; |
97 | |
98 | return 0; |
99 | } |
100 | |
101 | int octep_ctrl_net_init(struct octep_device *oct) |
102 | { |
103 | struct octep_ctrl_mbox *ctrl_mbox; |
104 | struct pci_dev *pdev = oct->pdev; |
105 | int ret; |
106 | |
107 | init_waitqueue_head(&oct->ctrl_req_wait_q); |
108 | INIT_LIST_HEAD(list: &oct->ctrl_req_wait_list); |
109 | |
110 | /* Initialize control mbox */ |
111 | ctrl_mbox = &oct->ctrl_mbox; |
112 | ctrl_mbox->version = OCTEP_CP_VERSION_CURRENT; |
113 | ctrl_mbox->barmem = CFG_GET_CTRL_MBOX_MEM_ADDR(oct->conf); |
114 | ret = octep_ctrl_mbox_init(mbox: ctrl_mbox); |
115 | if (ret) { |
116 | dev_err(&pdev->dev, "Failed to initialize control mbox\n" ); |
117 | return ret; |
118 | } |
119 | dev_info(&pdev->dev, "Control plane versions host: %llx, firmware: %x:%x\n" , |
120 | ctrl_mbox->version, ctrl_mbox->min_fw_version, |
121 | ctrl_mbox->max_fw_version); |
122 | oct->ctrl_mbox_ifstats_offset = ctrl_mbox->barmem_sz; |
123 | |
124 | return 0; |
125 | } |
126 | |
127 | int octep_ctrl_net_get_link_status(struct octep_device *oct, int vfid) |
128 | { |
129 | struct octep_ctrl_net_wait_data d = {}; |
130 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
131 | int err; |
132 | |
133 | init_send_req(msg: &d.msg, buf: (void *)req, sz: state_sz, vfid); |
134 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS; |
135 | req->link.cmd = OCTEP_CTRL_NET_CMD_GET; |
136 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
137 | if (err < 0) |
138 | return err; |
139 | |
140 | return d.data.resp.link.state; |
141 | } |
142 | |
143 | int octep_ctrl_net_set_link_status(struct octep_device *oct, int vfid, bool up, |
144 | bool wait_for_response) |
145 | { |
146 | struct octep_ctrl_net_wait_data d = {}; |
147 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
148 | |
149 | init_send_req(msg: &d.msg, buf: req, sz: state_sz, vfid); |
150 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_STATUS; |
151 | req->link.cmd = OCTEP_CTRL_NET_CMD_SET; |
152 | req->link.state = (up) ? OCTEP_CTRL_NET_STATE_UP : |
153 | OCTEP_CTRL_NET_STATE_DOWN; |
154 | |
155 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
156 | } |
157 | |
158 | int octep_ctrl_net_set_rx_state(struct octep_device *oct, int vfid, bool up, |
159 | bool wait_for_response) |
160 | { |
161 | struct octep_ctrl_net_wait_data d = {}; |
162 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
163 | |
164 | init_send_req(msg: &d.msg, buf: req, sz: state_sz, vfid); |
165 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_RX_STATE; |
166 | req->link.cmd = OCTEP_CTRL_NET_CMD_SET; |
167 | req->link.state = (up) ? OCTEP_CTRL_NET_STATE_UP : |
168 | OCTEP_CTRL_NET_STATE_DOWN; |
169 | |
170 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
171 | } |
172 | |
173 | int octep_ctrl_net_get_mac_addr(struct octep_device *oct, int vfid, u8 *addr) |
174 | { |
175 | struct octep_ctrl_net_wait_data d = {}; |
176 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
177 | int err; |
178 | |
179 | init_send_req(msg: &d.msg, buf: req, sz: mac_sz, vfid); |
180 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MAC; |
181 | req->link.cmd = OCTEP_CTRL_NET_CMD_GET; |
182 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
183 | if (err < 0) |
184 | return err; |
185 | |
186 | memcpy(addr, d.data.resp.mac.addr, ETH_ALEN); |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | int octep_ctrl_net_set_mac_addr(struct octep_device *oct, int vfid, u8 *addr, |
192 | bool wait_for_response) |
193 | { |
194 | struct octep_ctrl_net_wait_data d = {}; |
195 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
196 | |
197 | init_send_req(msg: &d.msg, buf: req, sz: mac_sz, vfid); |
198 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MAC; |
199 | req->mac.cmd = OCTEP_CTRL_NET_CMD_SET; |
200 | memcpy(&req->mac.addr, addr, ETH_ALEN); |
201 | |
202 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
203 | } |
204 | |
205 | int octep_ctrl_net_get_mtu(struct octep_device *oct, int vfid) |
206 | { |
207 | struct octep_ctrl_net_wait_data d = {}; |
208 | struct octep_ctrl_net_h2f_req *req; |
209 | int err; |
210 | |
211 | req = &d.data.req; |
212 | init_send_req(msg: &d.msg, buf: req, sz: mtu_sz, vfid); |
213 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MTU; |
214 | req->mtu.cmd = OCTEP_CTRL_NET_CMD_GET; |
215 | |
216 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
217 | if (err < 0) |
218 | return err; |
219 | |
220 | return d.data.resp.mtu.val; |
221 | } |
222 | |
223 | int octep_ctrl_net_set_mtu(struct octep_device *oct, int vfid, int mtu, |
224 | bool wait_for_response) |
225 | { |
226 | struct octep_ctrl_net_wait_data d = {}; |
227 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
228 | |
229 | init_send_req(msg: &d.msg, buf: req, sz: mtu_sz, vfid); |
230 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_MTU; |
231 | req->mtu.cmd = OCTEP_CTRL_NET_CMD_SET; |
232 | req->mtu.val = mtu; |
233 | |
234 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
235 | } |
236 | |
237 | int octep_ctrl_net_get_if_stats(struct octep_device *oct, int vfid, |
238 | struct octep_iface_rx_stats *rx_stats, |
239 | struct octep_iface_tx_stats *tx_stats) |
240 | { |
241 | struct octep_ctrl_net_wait_data d = {}; |
242 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
243 | struct octep_ctrl_net_h2f_resp *resp; |
244 | int err; |
245 | |
246 | init_send_req(msg: &d.msg, buf: req, sz: 0, vfid); |
247 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_GET_IF_STATS; |
248 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
249 | if (err < 0) |
250 | return err; |
251 | |
252 | resp = &d.data.resp; |
253 | memcpy(rx_stats, &resp->if_stats.rx_stats, sizeof(struct octep_iface_rx_stats)); |
254 | memcpy(tx_stats, &resp->if_stats.tx_stats, sizeof(struct octep_iface_tx_stats)); |
255 | return 0; |
256 | } |
257 | |
258 | int octep_ctrl_net_get_link_info(struct octep_device *oct, int vfid, |
259 | struct octep_iface_link_info *link_info) |
260 | { |
261 | struct octep_ctrl_net_wait_data d = {}; |
262 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
263 | struct octep_ctrl_net_h2f_resp *resp; |
264 | int err; |
265 | |
266 | init_send_req(msg: &d.msg, buf: req, sz: link_info_sz, vfid); |
267 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO; |
268 | req->link_info.cmd = OCTEP_CTRL_NET_CMD_GET; |
269 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
270 | if (err < 0) |
271 | return err; |
272 | |
273 | resp = &d.data.resp; |
274 | link_info->supported_modes = resp->link_info.supported_modes; |
275 | link_info->advertised_modes = resp->link_info.advertised_modes; |
276 | link_info->autoneg = resp->link_info.autoneg; |
277 | link_info->pause = resp->link_info.pause; |
278 | link_info->speed = resp->link_info.speed; |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | int octep_ctrl_net_set_link_info(struct octep_device *oct, int vfid, |
284 | struct octep_iface_link_info *link_info, |
285 | bool wait_for_response) |
286 | { |
287 | struct octep_ctrl_net_wait_data d = {}; |
288 | struct octep_ctrl_net_h2f_req *req = &d.data.req; |
289 | |
290 | init_send_req(msg: &d.msg, buf: req, sz: link_info_sz, vfid); |
291 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_LINK_INFO; |
292 | req->link_info.cmd = OCTEP_CTRL_NET_CMD_SET; |
293 | req->link_info.info.advertised_modes = link_info->advertised_modes; |
294 | req->link_info.info.autoneg = link_info->autoneg; |
295 | req->link_info.info.pause = link_info->pause; |
296 | req->link_info.info.speed = link_info->speed; |
297 | |
298 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
299 | } |
300 | |
301 | static void process_mbox_resp(struct octep_device *oct, |
302 | struct octep_ctrl_mbox_msg *msg) |
303 | { |
304 | struct octep_ctrl_net_wait_data *pos, *n; |
305 | |
306 | list_for_each_entry_safe(pos, n, &oct->ctrl_req_wait_list, list) { |
307 | if (pos->msg.hdr.s.msg_id == msg->hdr.s.msg_id) { |
308 | memcpy(&pos->data.resp, |
309 | msg->sg_list[0].msg, |
310 | msg->hdr.s.sz); |
311 | pos->done = 1; |
312 | wake_up_interruptible_all(&oct->ctrl_req_wait_q); |
313 | break; |
314 | } |
315 | } |
316 | } |
317 | |
318 | static int process_mbox_notify(struct octep_device *oct, |
319 | struct octep_ctrl_mbox_msg *msg) |
320 | { |
321 | struct net_device *netdev = oct->netdev; |
322 | struct octep_ctrl_net_f2h_req *req; |
323 | int cmd; |
324 | |
325 | req = (struct octep_ctrl_net_f2h_req *)msg->sg_list[0].msg; |
326 | cmd = req->hdr.s.cmd; |
327 | |
328 | /* check if we support this command */ |
329 | if (octep_ctrl_net_f2h_cmd_versions[cmd] > OCTEP_CP_VERSION_CURRENT || |
330 | octep_ctrl_net_f2h_cmd_versions[cmd] < OCTEP_CP_VERSION_CURRENT) |
331 | return -EOPNOTSUPP; |
332 | |
333 | if (msg->hdr.s.is_vf) { |
334 | octep_pfvf_notify(oct, msg); |
335 | return 0; |
336 | } |
337 | |
338 | switch (cmd) { |
339 | case OCTEP_CTRL_NET_F2H_CMD_LINK_STATUS: |
340 | if (netif_running(dev: netdev)) { |
341 | if (req->link.state) { |
342 | dev_info(&oct->pdev->dev, "netif_carrier_on\n" ); |
343 | netif_carrier_on(dev: netdev); |
344 | } else { |
345 | dev_info(&oct->pdev->dev, "netif_carrier_off\n" ); |
346 | netif_carrier_off(dev: netdev); |
347 | } |
348 | } |
349 | break; |
350 | default: |
351 | pr_info("Unknown mbox req : %u\n" , req->hdr.s.cmd); |
352 | break; |
353 | } |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | void octep_ctrl_net_recv_fw_messages(struct octep_device *oct) |
359 | { |
360 | static u16 msg_sz = sizeof(union octep_ctrl_net_max_data); |
361 | union octep_ctrl_net_max_data data = {}; |
362 | struct octep_ctrl_mbox_msg msg = {}; |
363 | int ret; |
364 | |
365 | msg.hdr.s.sz = msg_sz; |
366 | msg.sg_num = 1; |
367 | msg.sg_list[0].sz = msg_sz; |
368 | msg.sg_list[0].msg = &data; |
369 | while (true) { |
370 | /* mbox will overwrite msg.hdr.s.sz so initialize it */ |
371 | msg.hdr.s.sz = msg_sz; |
372 | ret = octep_ctrl_mbox_recv(mbox: &oct->ctrl_mbox, msg: (struct octep_ctrl_mbox_msg *)&msg); |
373 | if (ret < 0) |
374 | break; |
375 | |
376 | if (msg.hdr.s.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_RESP) |
377 | process_mbox_resp(oct, msg: &msg); |
378 | else if (msg.hdr.s.flags & OCTEP_CTRL_MBOX_MSG_HDR_FLAG_NOTIFY) |
379 | process_mbox_notify(oct, msg: &msg); |
380 | } |
381 | } |
382 | |
383 | int octep_ctrl_net_get_info(struct octep_device *oct, int vfid, |
384 | struct octep_fw_info *info) |
385 | { |
386 | struct octep_ctrl_net_wait_data d = {}; |
387 | struct octep_ctrl_net_h2f_resp *resp; |
388 | struct octep_ctrl_net_h2f_req *req; |
389 | int err; |
390 | |
391 | req = &d.data.req; |
392 | init_send_req(msg: &d.msg, buf: req, sz: 0, vfid); |
393 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_GET_INFO; |
394 | req->link_info.cmd = OCTEP_CTRL_NET_CMD_GET; |
395 | err = octep_send_mbox_req(oct, d: &d, wait_for_response: true); |
396 | if (err < 0) |
397 | return err; |
398 | |
399 | resp = &d.data.resp; |
400 | memcpy(info, &resp->info.fw_info, sizeof(struct octep_fw_info)); |
401 | |
402 | return 0; |
403 | } |
404 | |
405 | int octep_ctrl_net_dev_remove(struct octep_device *oct, int vfid) |
406 | { |
407 | struct octep_ctrl_net_wait_data d = {}; |
408 | struct octep_ctrl_net_h2f_req *req; |
409 | |
410 | req = &d.data.req; |
411 | dev_dbg(&oct->pdev->dev, "Sending dev_unload msg to fw\n" ); |
412 | init_send_req(msg: &d.msg, buf: req, sz: sizeof(int), vfid); |
413 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_DEV_REMOVE; |
414 | |
415 | return octep_send_mbox_req(oct, d: &d, wait_for_response: false); |
416 | } |
417 | |
418 | int octep_ctrl_net_set_offloads(struct octep_device *oct, int vfid, |
419 | struct octep_ctrl_net_offloads *offloads, |
420 | bool wait_for_response) |
421 | { |
422 | struct octep_ctrl_net_wait_data d = {}; |
423 | struct octep_ctrl_net_h2f_req *req; |
424 | |
425 | req = &d.data.req; |
426 | init_send_req(msg: &d.msg, buf: req, sz: offloads_sz, vfid); |
427 | req->hdr.s.cmd = OCTEP_CTRL_NET_H2F_CMD_OFFLOADS; |
428 | req->offloads.cmd = OCTEP_CTRL_NET_CMD_SET; |
429 | req->offloads.offloads = *offloads; |
430 | |
431 | return octep_send_mbox_req(oct, d: &d, wait_for_response); |
432 | } |
433 | |
434 | int octep_ctrl_net_uninit(struct octep_device *oct) |
435 | { |
436 | struct octep_ctrl_net_wait_data *pos, *n; |
437 | |
438 | octep_ctrl_net_dev_remove(oct, OCTEP_CTRL_NET_INVALID_VFID); |
439 | |
440 | list_for_each_entry_safe(pos, n, &oct->ctrl_req_wait_list, list) |
441 | pos->done = 1; |
442 | |
443 | wake_up_interruptible_all(&oct->ctrl_req_wait_q); |
444 | |
445 | octep_ctrl_mbox_uninit(mbox: &oct->ctrl_mbox); |
446 | |
447 | return 0; |
448 | } |
449 | |