blob: f1be3e3f6425e253df876ceee8a490e3eed98765 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Gavin Shan2d283bd2016-07-19 11:54:16 +10002/*
3 * Copyright Gavin Shan, IBM Corporation 2016.
Gavin Shan2d283bd2016-07-19 11:54:16 +10004 */
5
6#include <linux/module.h>
7#include <linux/kernel.h>
8#include <linux/init.h>
9#include <linux/netdevice.h>
10#include <linux/skbuff.h>
Vijay Khemka5e0fcc12020-01-08 15:43:40 -080011#include <linux/of.h>
12#include <linux/platform_device.h>
Gavin Shan2d283bd2016-07-19 11:54:16 +100013
14#include <net/ncsi.h>
15#include <net/net_namespace.h>
16#include <net/sock.h>
Gavin Shane6f44ed2016-07-19 11:54:19 +100017#include <net/addrconf.h>
18#include <net/ipv6.h>
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +000019#include <net/genetlink.h>
Gavin Shan2d283bd2016-07-19 11:54:16 +100020
21#include "internal.h"
Gavin Shane6f44ed2016-07-19 11:54:19 +100022#include "ncsi-pkt.h"
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +110023#include "ncsi-netlink.h"
Gavin Shan2d283bd2016-07-19 11:54:16 +100024
25LIST_HEAD(ncsi_dev_list);
26DEFINE_SPINLOCK(ncsi_dev_lock);
27
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +110028bool ncsi_channel_has_link(struct ncsi_channel *channel)
29{
30 return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
31}
32
33bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
34 struct ncsi_channel *channel)
35{
36 struct ncsi_package *np;
37 struct ncsi_channel *nc;
38
39 NCSI_FOR_EACH_PACKAGE(ndp, np)
40 NCSI_FOR_EACH_CHANNEL(np, nc) {
41 if (nc == channel)
42 continue;
43 if (nc->state == NCSI_CHANNEL_ACTIVE &&
44 ncsi_channel_has_link(nc))
45 return false;
46 }
47
48 return true;
49}
50
Gavin Shane6f44ed2016-07-19 11:54:19 +100051static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
52{
53 struct ncsi_dev *nd = &ndp->ndev;
54 struct ncsi_package *np;
55 struct ncsi_channel *nc;
Gavin Shand8cedaa2016-10-04 11:25:47 +110056 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +100057
58 nd->state = ncsi_dev_state_functional;
59 if (force_down) {
60 nd->link_up = 0;
61 goto report;
62 }
63
64 nd->link_up = 0;
65 NCSI_FOR_EACH_PACKAGE(ndp, np) {
66 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shand8cedaa2016-10-04 11:25:47 +110067 spin_lock_irqsave(&nc->lock, flags);
68
Gavin Shane6f44ed2016-07-19 11:54:19 +100069 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +110070 nc->state != NCSI_CHANNEL_ACTIVE) {
71 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100072 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +110073 }
Gavin Shane6f44ed2016-07-19 11:54:19 +100074
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +110075 if (ncsi_channel_has_link(nc)) {
Gavin Shand8cedaa2016-10-04 11:25:47 +110076 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100077 nd->link_up = 1;
78 goto report;
79 }
Gavin Shand8cedaa2016-10-04 11:25:47 +110080
81 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100082 }
83 }
84
85report:
86 nd->handler(nd);
87}
88
Kees Cook86cb30e2017-10-17 20:21:24 -070089static void ncsi_channel_monitor(struct timer_list *t)
Gavin Shane6f44ed2016-07-19 11:54:19 +100090{
Kees Cook86cb30e2017-10-17 20:21:24 -070091 struct ncsi_channel *nc = from_timer(nc, t, monitor.timer);
Gavin Shane6f44ed2016-07-19 11:54:19 +100092 struct ncsi_package *np = nc->package;
93 struct ncsi_dev_priv *ndp = np->ndp;
Gavin Shan52b4c862017-10-19 13:43:08 +110094 struct ncsi_channel_mode *ncm;
Gavin Shane6f44ed2016-07-19 11:54:19 +100095 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +110096 bool enabled, chained;
Gavin Shan83afdc62016-10-04 11:25:52 +110097 unsigned int monitor_state;
Gavin Shane6f44ed2016-07-19 11:54:19 +100098 unsigned long flags;
Gavin Shand8cedaa2016-10-04 11:25:47 +110099 int state, ret;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000100
101 spin_lock_irqsave(&nc->lock, flags);
Gavin Shand8cedaa2016-10-04 11:25:47 +1100102 state = nc->state;
103 chained = !list_empty(&nc->link);
Gavin Shan83afdc62016-10-04 11:25:52 +1100104 enabled = nc->monitor.enabled;
105 monitor_state = nc->monitor.state;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000106 spin_unlock_irqrestore(&nc->lock, flags);
107
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100108 if (!enabled || chained) {
109 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000110 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100111 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100112 if (state != NCSI_CHANNEL_INACTIVE &&
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100113 state != NCSI_CHANNEL_ACTIVE) {
114 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000115 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100116 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000117
Gavin Shan83afdc62016-10-04 11:25:52 +1100118 switch (monitor_state) {
119 case NCSI_CHANNEL_MONITOR_START:
120 case NCSI_CHANNEL_MONITOR_RETRY:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000121 nca.ndp = ndp;
122 nca.package = np->id;
123 nca.channel = nc->id;
124 nca.type = NCSI_PKT_CMD_GLS;
Gavin Shana0509cb2016-10-04 11:25:51 +1100125 nca.req_flags = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000126 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100127 if (ret)
Gavin Shane6f44ed2016-07-19 11:54:19 +1000128 netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
129 ret);
Gavin Shan83afdc62016-10-04 11:25:52 +1100130 break;
131 case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
132 break;
133 default:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100134 netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
135 nc->id);
Samuel Mendoza-Jonas60ab49b2018-11-16 15:51:54 +1100136 ncsi_report_link(ndp, true);
137 ndp->flags |= NCSI_DEV_RESHUFFLE;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000138
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100139 ncsi_stop_channel_monitor(nc);
140
Gavin Shan52b4c862017-10-19 13:43:08 +1100141 ncm = &nc->modes[NCSI_MODE_LINK];
Gavin Shand8cedaa2016-10-04 11:25:47 +1100142 spin_lock_irqsave(&nc->lock, flags);
143 nc->state = NCSI_CHANNEL_INVISIBLE;
Gavin Shan52b4c862017-10-19 13:43:08 +1100144 ncm->data[2] &= ~0x1;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100145 spin_unlock_irqrestore(&nc->lock, flags);
146
Gavin Shane6f44ed2016-07-19 11:54:19 +1000147 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shan52b4c862017-10-19 13:43:08 +1100148 nc->state = NCSI_CHANNEL_ACTIVE;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000149 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
150 spin_unlock_irqrestore(&ndp->lock, flags);
151 ncsi_process_next_channel(ndp);
152 return;
153 }
154
155 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100156 nc->monitor.state++;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000157 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100158 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000159}
160
161void ncsi_start_channel_monitor(struct ncsi_channel *nc)
162{
163 unsigned long flags;
164
165 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100166 WARN_ON_ONCE(nc->monitor.enabled);
167 nc->monitor.enabled = true;
168 nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000169 spin_unlock_irqrestore(&nc->lock, flags);
170
Gavin Shan83afdc62016-10-04 11:25:52 +1100171 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000172}
173
174void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
175{
176 unsigned long flags;
177
178 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100179 if (!nc->monitor.enabled) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000180 spin_unlock_irqrestore(&nc->lock, flags);
181 return;
182 }
Gavin Shan83afdc62016-10-04 11:25:52 +1100183 nc->monitor.enabled = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000184 spin_unlock_irqrestore(&nc->lock, flags);
185
Gavin Shan83afdc62016-10-04 11:25:52 +1100186 del_timer_sync(&nc->monitor.timer);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000187}
188
Gavin Shan2d283bd2016-07-19 11:54:16 +1000189struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
190 unsigned char id)
191{
192 struct ncsi_channel *nc;
193
194 NCSI_FOR_EACH_CHANNEL(np, nc) {
195 if (nc->id == id)
196 return nc;
197 }
198
199 return NULL;
200}
201
202struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
203{
204 struct ncsi_channel *nc, *tmp;
205 int index;
206 unsigned long flags;
207
208 nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
209 if (!nc)
210 return NULL;
211
212 nc->id = id;
213 nc->package = np;
214 nc->state = NCSI_CHANNEL_INACTIVE;
Gavin Shan83afdc62016-10-04 11:25:52 +1100215 nc->monitor.enabled = false;
Kees Cook86cb30e2017-10-17 20:21:24 -0700216 timer_setup(&nc->monitor.timer, ncsi_channel_monitor, 0);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000217 spin_lock_init(&nc->lock);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000218 INIT_LIST_HEAD(&nc->link);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000219 for (index = 0; index < NCSI_CAP_MAX; index++)
220 nc->caps[index].index = index;
221 for (index = 0; index < NCSI_MODE_MAX; index++)
222 nc->modes[index].index = index;
223
224 spin_lock_irqsave(&np->lock, flags);
225 tmp = ncsi_find_channel(np, id);
226 if (tmp) {
227 spin_unlock_irqrestore(&np->lock, flags);
228 kfree(nc);
229 return tmp;
230 }
231
232 list_add_tail_rcu(&nc->node, &np->channels);
233 np->channel_num++;
234 spin_unlock_irqrestore(&np->lock, flags);
235
236 return nc;
237}
238
239static void ncsi_remove_channel(struct ncsi_channel *nc)
240{
241 struct ncsi_package *np = nc->package;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000242 unsigned long flags;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000243
244 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000245
246 /* Release filters */
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000247 kfree(nc->mac_filter.addrs);
248 kfree(nc->vlan_filter.vids);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000249
250 nc->state = NCSI_CHANNEL_INACTIVE;
251 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000252 ncsi_stop_channel_monitor(nc);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000253
254 /* Remove and free channel */
255 spin_lock_irqsave(&np->lock, flags);
256 list_del_rcu(&nc->node);
257 np->channel_num--;
258 spin_unlock_irqrestore(&np->lock, flags);
259
260 kfree(nc);
261}
262
263struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
264 unsigned char id)
265{
266 struct ncsi_package *np;
267
268 NCSI_FOR_EACH_PACKAGE(ndp, np) {
269 if (np->id == id)
270 return np;
271 }
272
273 return NULL;
274}
275
276struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
277 unsigned char id)
278{
279 struct ncsi_package *np, *tmp;
280 unsigned long flags;
281
282 np = kzalloc(sizeof(*np), GFP_ATOMIC);
283 if (!np)
284 return NULL;
285
286 np->id = id;
287 np->ndp = ndp;
288 spin_lock_init(&np->lock);
289 INIT_LIST_HEAD(&np->channels);
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +1100290 np->channel_whitelist = UINT_MAX;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000291
292 spin_lock_irqsave(&ndp->lock, flags);
293 tmp = ncsi_find_package(ndp, id);
294 if (tmp) {
295 spin_unlock_irqrestore(&ndp->lock, flags);
296 kfree(np);
297 return tmp;
298 }
299
300 list_add_tail_rcu(&np->node, &ndp->packages);
301 ndp->package_num++;
302 spin_unlock_irqrestore(&ndp->lock, flags);
303
304 return np;
305}
306
307void ncsi_remove_package(struct ncsi_package *np)
308{
309 struct ncsi_dev_priv *ndp = np->ndp;
310 struct ncsi_channel *nc, *tmp;
311 unsigned long flags;
312
313 /* Release all child channels */
314 list_for_each_entry_safe(nc, tmp, &np->channels, node)
315 ncsi_remove_channel(nc);
316
317 /* Remove and free package */
318 spin_lock_irqsave(&ndp->lock, flags);
319 list_del_rcu(&np->node);
320 ndp->package_num--;
321 spin_unlock_irqrestore(&ndp->lock, flags);
322
323 kfree(np);
324}
325
326void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
327 unsigned char id,
328 struct ncsi_package **np,
329 struct ncsi_channel **nc)
330{
331 struct ncsi_package *p;
332 struct ncsi_channel *c;
333
334 p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
335 c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
336
337 if (np)
338 *np = p;
339 if (nc)
340 *nc = c;
341}
342
343/* For two consecutive NCSI commands, the packet IDs shouldn't
344 * be same. Otherwise, the bogus response might be replied. So
345 * the available IDs are allocated in round-robin fashion.
346 */
Gavin Shana0509cb2016-10-04 11:25:51 +1100347struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
348 unsigned int req_flags)
Gavin Shan2d283bd2016-07-19 11:54:16 +1000349{
350 struct ncsi_request *nr = NULL;
351 int i, limit = ARRAY_SIZE(ndp->requests);
352 unsigned long flags;
353
354 /* Check if there is one available request until the ceiling */
355 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shana15af542016-10-04 11:25:50 +1100356 for (i = ndp->request_id; i < limit; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000357 if (ndp->requests[i].used)
358 continue;
359
360 nr = &ndp->requests[i];
361 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100362 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100363 ndp->request_id = i + 1;
364 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000365 }
366
367 /* Fail back to check from the starting cursor */
Gavin Shana15af542016-10-04 11:25:50 +1100368 for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000369 if (ndp->requests[i].used)
370 continue;
371
372 nr = &ndp->requests[i];
373 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100374 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100375 ndp->request_id = i + 1;
376 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000377 }
Gavin Shan2d283bd2016-07-19 11:54:16 +1000378
Gavin Shana15af542016-10-04 11:25:50 +1100379found:
380 spin_unlock_irqrestore(&ndp->lock, flags);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000381 return nr;
382}
383
384void ncsi_free_request(struct ncsi_request *nr)
385{
386 struct ncsi_dev_priv *ndp = nr->ndp;
387 struct sk_buff *cmd, *rsp;
388 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000389 bool driven;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000390
391 if (nr->enabled) {
392 nr->enabled = false;
393 del_timer_sync(&nr->timer);
394 }
395
396 spin_lock_irqsave(&ndp->lock, flags);
397 cmd = nr->cmd;
398 rsp = nr->rsp;
399 nr->cmd = NULL;
400 nr->rsp = NULL;
401 nr->used = false;
Gavin Shana0509cb2016-10-04 11:25:51 +1100402 driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000403 spin_unlock_irqrestore(&ndp->lock, flags);
404
Gavin Shane6f44ed2016-07-19 11:54:19 +1000405 if (driven && cmd && --ndp->pending_req_num == 0)
406 schedule_work(&ndp->work);
407
Gavin Shan2d283bd2016-07-19 11:54:16 +1000408 /* Release command and response */
409 consume_skb(cmd);
410 consume_skb(rsp);
411}
412
413struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
414{
415 struct ncsi_dev_priv *ndp;
416
417 NCSI_FOR_EACH_DEV(ndp) {
418 if (ndp->ndev.dev == dev)
419 return &ndp->ndev;
420 }
421
422 return NULL;
423}
424
Kees Cooke99e88a2017-10-16 14:43:17 -0700425static void ncsi_request_timeout(struct timer_list *t)
Gavin Shan2d283bd2016-07-19 11:54:16 +1000426{
Kees Cooke99e88a2017-10-16 14:43:17 -0700427 struct ncsi_request *nr = from_timer(nr, t, timer);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000428 struct ncsi_dev_priv *ndp = nr->ndp;
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +0000429 struct ncsi_cmd_pkt *cmd;
430 struct ncsi_package *np;
431 struct ncsi_channel *nc;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000432 unsigned long flags;
433
434 /* If the request already had associated response,
435 * let the response handler to release it.
436 */
437 spin_lock_irqsave(&ndp->lock, flags);
438 nr->enabled = false;
439 if (nr->rsp || !nr->cmd) {
440 spin_unlock_irqrestore(&ndp->lock, flags);
441 return;
442 }
443 spin_unlock_irqrestore(&ndp->lock, flags);
444
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +0000445 if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
446 if (nr->cmd) {
447 /* Find the package */
448 cmd = (struct ncsi_cmd_pkt *)
449 skb_network_header(nr->cmd);
450 ncsi_find_package_and_channel(ndp,
451 cmd->cmd.common.channel,
452 &np, &nc);
453 ncsi_send_netlink_timeout(nr, np, nc);
454 }
455 }
456
Gavin Shan2d283bd2016-07-19 11:54:16 +1000457 /* Release the request */
458 ncsi_free_request(nr);
459}
460
Gavin Shane6f44ed2016-07-19 11:54:19 +1000461static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
462{
463 struct ncsi_dev *nd = &ndp->ndev;
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100464 struct ncsi_package *np;
465 struct ncsi_channel *nc, *tmp;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000466 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100467 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000468 int ret;
469
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100470 np = ndp->active_package;
471 nc = ndp->active_channel;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000472 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100473 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000474 switch (nd->state) {
475 case ncsi_dev_state_suspend:
476 nd->state = ncsi_dev_state_suspend_select;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500477 fallthrough;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000478 case ncsi_dev_state_suspend_select:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100479 ndp->pending_req_num = 1;
480
481 nca.type = NCSI_PKT_CMD_SP;
482 nca.package = np->id;
483 nca.channel = NCSI_RESERVED_CHANNEL;
484 if (ndp->flags & NCSI_DEV_HWA)
485 nca.bytes[0] = 0;
486 else
487 nca.bytes[0] = 1;
488
Gavin Shan008a4242016-10-20 11:45:50 +1100489 /* To retrieve the last link states of channels in current
490 * package when current active channel needs fail over to
491 * another one. It means we will possibly select another
492 * channel as next active one. The link states of channels
493 * are most important factor of the selection. So we need
494 * accurate link states. Unfortunately, the link states on
495 * inactive channels can't be updated with LSC AEN in time.
496 */
497 if (ndp->flags & NCSI_DEV_RESHUFFLE)
498 nd->state = ncsi_dev_state_suspend_gls;
499 else
500 nd->state = ncsi_dev_state_suspend_dcnt;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100501 ret = ncsi_xmit_cmd(&nca);
502 if (ret)
503 goto error;
504
505 break;
Gavin Shan008a4242016-10-20 11:45:50 +1100506 case ncsi_dev_state_suspend_gls:
507 ndp->pending_req_num = np->channel_num;
508
509 nca.type = NCSI_PKT_CMD_GLS;
510 nca.package = np->id;
511
512 nd->state = ncsi_dev_state_suspend_dcnt;
513 NCSI_FOR_EACH_CHANNEL(np, nc) {
514 nca.channel = nc->id;
515 ret = ncsi_xmit_cmd(&nca);
516 if (ret)
517 goto error;
518 }
519
520 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000521 case ncsi_dev_state_suspend_dcnt:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100522 ndp->pending_req_num = 1;
523
524 nca.type = NCSI_PKT_CMD_DCNT;
525 nca.package = np->id;
526 nca.channel = nc->id;
527
528 nd->state = ncsi_dev_state_suspend_dc;
529 ret = ncsi_xmit_cmd(&nca);
530 if (ret)
531 goto error;
532
533 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000534 case ncsi_dev_state_suspend_dc:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100535 ndp->pending_req_num = 1;
536
537 nca.type = NCSI_PKT_CMD_DC;
538 nca.package = np->id;
539 nca.channel = nc->id;
540 nca.bytes[0] = 1;
541
542 nd->state = ncsi_dev_state_suspend_deselect;
543 ret = ncsi_xmit_cmd(&nca);
544 if (ret)
545 goto error;
546
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100547 NCSI_FOR_EACH_CHANNEL(np, tmp) {
548 /* If there is another channel active on this package
549 * do not deselect the package.
550 */
551 if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
552 nd->state = ncsi_dev_state_suspend_done;
553 break;
554 }
555 }
Gavin Shan7ba5c002016-10-20 11:45:49 +1100556 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000557 case ncsi_dev_state_suspend_deselect:
558 ndp->pending_req_num = 1;
559
Gavin Shan7ba5c002016-10-20 11:45:49 +1100560 nca.type = NCSI_PKT_CMD_DP;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000561 nca.package = np->id;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100562 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000563
Gavin Shan7ba5c002016-10-20 11:45:49 +1100564 nd->state = ncsi_dev_state_suspend_done;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000565 ret = ncsi_xmit_cmd(&nca);
Gavin Shan7ba5c002016-10-20 11:45:49 +1100566 if (ret)
567 goto error;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000568
569 break;
570 case ncsi_dev_state_suspend_done:
Gavin Shand8cedaa2016-10-04 11:25:47 +1100571 spin_lock_irqsave(&nc->lock, flags);
572 nc->state = NCSI_CHANNEL_INACTIVE;
573 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +1100574 if (ndp->flags & NCSI_DEV_RESET)
575 ncsi_reset_dev(nd);
576 else
577 ncsi_process_next_channel(ndp);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000578 break;
579 default:
580 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
581 nd->state);
582 }
Gavin Shan7ba5c002016-10-20 11:45:49 +1100583
584 return;
585error:
586 nd->state = ncsi_dev_state_functional;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000587}
588
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000589/* Check the VLAN filter bitmap for a set filter, and construct a
590 * "Set VLAN Filter - Disable" packet if found.
591 */
592static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
593 struct ncsi_cmd_arg *nca)
594{
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000595 struct ncsi_channel_vlan_filter *ncf;
596 unsigned long flags;
597 void *bitmap;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000598 int index;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000599 u16 vid;
600
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000601 ncf = &nc->vlan_filter;
602 bitmap = &ncf->bitmap;
603
604 spin_lock_irqsave(&nc->lock, flags);
605 index = find_next_bit(bitmap, ncf->n_vids, 0);
606 if (index >= ncf->n_vids) {
607 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000608 return -1;
609 }
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000610 vid = ncf->vids[index];
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000611
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000612 clear_bit(index, bitmap);
613 ncf->vids[index] = 0;
614 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000615
616 nca->type = NCSI_PKT_CMD_SVF;
617 nca->words[1] = vid;
618 /* HW filter index starts at 1 */
619 nca->bytes[6] = index + 1;
620 nca->bytes[7] = 0x00;
621 return 0;
622}
623
624/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
625 * packet.
626 */
627static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
628 struct ncsi_cmd_arg *nca)
629{
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000630 struct ncsi_channel_vlan_filter *ncf;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000631 struct vlan_vid *vlan = NULL;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000632 unsigned long flags;
633 int i, index;
634 void *bitmap;
635 u16 vid;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000636
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000637 if (list_empty(&ndp->vlan_vids))
638 return -1;
639
640 ncf = &nc->vlan_filter;
641 bitmap = &ncf->bitmap;
642
643 spin_lock_irqsave(&nc->lock, flags);
644
645 rcu_read_lock();
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000646 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000647 vid = vlan->vid;
648 for (i = 0; i < ncf->n_vids; i++)
649 if (ncf->vids[i] == vid) {
650 vid = 0;
651 break;
652 }
653 if (vid)
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000654 break;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000655 }
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000656 rcu_read_unlock();
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000657
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000658 if (!vid) {
659 /* No VLAN ID is not set */
660 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000661 return -1;
662 }
663
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000664 index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
665 if (index < 0 || index >= ncf->n_vids) {
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000666 netdev_err(ndp->ndev.dev,
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000667 "Channel %u already has all VLAN filters set\n",
668 nc->id);
669 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000670 return -1;
671 }
672
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000673 ncf->vids[index] = vid;
674 set_bit(index, bitmap);
675 spin_unlock_irqrestore(&nc->lock, flags);
676
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000677 nca->type = NCSI_PKT_CMD_SVF;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000678 nca->words[1] = vid;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000679 /* HW filter index starts at 1 */
680 nca->bytes[6] = index + 1;
681 nca->bytes[7] = 0x01;
682
683 return 0;
684}
685
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700686#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
687
688/* NCSI OEM Command APIs */
689static int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
690{
691 unsigned char data[NCSI_OEM_BCM_CMD_GMA_LEN];
692 int ret = 0;
693
694 nca->payload = NCSI_OEM_BCM_CMD_GMA_LEN;
695
696 memset(data, 0, NCSI_OEM_BCM_CMD_GMA_LEN);
697 *(unsigned int *)data = ntohl(NCSI_OEM_MFR_BCM_ID);
698 data[5] = NCSI_OEM_BCM_CMD_GMA;
699
700 nca->data = data;
701
702 ret = ncsi_xmit_cmd(nca);
703 if (ret)
704 netdev_err(nca->ndp->ndev.dev,
705 "NCSI: Failed to transmit cmd 0x%x during configure\n",
706 nca->type);
707 return ret;
708}
709
Vijay Khemka16e8c4c2018-11-26 13:49:04 -0800710static int ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca)
711{
712 union {
713 u8 data_u8[NCSI_OEM_MLX_CMD_GMA_LEN];
714 u32 data_u32[NCSI_OEM_MLX_CMD_GMA_LEN / sizeof(u32)];
715 } u;
716 int ret = 0;
717
718 nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN;
719
720 memset(&u, 0, sizeof(u));
721 u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
722 u.data_u8[5] = NCSI_OEM_MLX_CMD_GMA;
723 u.data_u8[6] = NCSI_OEM_MLX_CMD_GMA_PARAM;
724
725 nca->data = u.data_u8;
726
727 ret = ncsi_xmit_cmd(nca);
728 if (ret)
729 netdev_err(nca->ndp->ndev.dev,
730 "NCSI: Failed to transmit cmd 0x%x during configure\n",
731 nca->type);
732 return ret;
733}
734
Vijay Khemka5e0fcc12020-01-08 15:43:40 -0800735static int ncsi_oem_smaf_mlx(struct ncsi_cmd_arg *nca)
736{
737 union {
738 u8 data_u8[NCSI_OEM_MLX_CMD_SMAF_LEN];
739 u32 data_u32[NCSI_OEM_MLX_CMD_SMAF_LEN / sizeof(u32)];
740 } u;
741 int ret = 0;
742
743 memset(&u, 0, sizeof(u));
744 u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
745 u.data_u8[5] = NCSI_OEM_MLX_CMD_SMAF;
746 u.data_u8[6] = NCSI_OEM_MLX_CMD_SMAF_PARAM;
747 memcpy(&u.data_u8[MLX_SMAF_MAC_ADDR_OFFSET],
748 nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
749 u.data_u8[MLX_SMAF_MED_SUPPORT_OFFSET] =
750 (MLX_MC_RBT_AVL | MLX_MC_RBT_SUPPORT);
751
752 nca->payload = NCSI_OEM_MLX_CMD_SMAF_LEN;
753 nca->data = u.data_u8;
754
755 ret = ncsi_xmit_cmd(nca);
756 if (ret)
757 netdev_err(nca->ndp->ndev.dev,
758 "NCSI: Failed to transmit cmd 0x%x during probe\n",
759 nca->type);
760 return ret;
761}
762
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700763/* OEM Command handlers initialization */
764static struct ncsi_oem_gma_handler {
765 unsigned int mfr_id;
766 int (*handler)(struct ncsi_cmd_arg *nca);
767} ncsi_oem_gma_handlers[] = {
Vijay Khemka16e8c4c2018-11-26 13:49:04 -0800768 { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm },
769 { NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx }
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700770};
771
772static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
773{
774 struct ncsi_oem_gma_handler *nch = NULL;
775 int i;
776
777 /* This function should only be called once, return if flag set */
778 if (nca->ndp->gma_flag == 1)
779 return -1;
780
781 /* Find gma handler for given manufacturer id */
782 for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) {
783 if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) {
784 if (ncsi_oem_gma_handlers[i].handler)
785 nch = &ncsi_oem_gma_handlers[i];
786 break;
787 }
788 }
789
790 if (!nch) {
791 netdev_err(nca->ndp->ndev.dev,
792 "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
793 mf_id);
794 return -1;
795 }
796
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700797 /* Get Mac address from NCSI device */
798 return nch->handler(nca);
799}
800
801#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
802
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +1100803/* Determine if a given channel from the channel_queue should be used for Tx */
804static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
805 struct ncsi_channel *nc)
806{
807 struct ncsi_channel_mode *ncm;
808 struct ncsi_channel *channel;
809 struct ncsi_package *np;
810
811 /* Check if any other channel has Tx enabled; a channel may have already
812 * been configured and removed from the channel queue.
813 */
814 NCSI_FOR_EACH_PACKAGE(ndp, np) {
815 if (!ndp->multi_package && np != nc->package)
816 continue;
817 NCSI_FOR_EACH_CHANNEL(np, channel) {
818 ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
819 if (ncm->enable)
820 return false;
821 }
822 }
823
824 /* This channel is the preferred channel and has link */
825 list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
826 np = channel->package;
827 if (np->preferred_channel &&
828 ncsi_channel_has_link(np->preferred_channel)) {
829 return np->preferred_channel == nc;
830 }
831 }
832
833 /* This channel has link */
834 if (ncsi_channel_has_link(nc))
835 return true;
836
837 list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
838 if (ncsi_channel_has_link(channel))
839 return false;
840
841 /* No other channel has link; default to this one */
842 return true;
843}
844
845/* Change the active Tx channel in a multi-channel setup */
846int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
847 struct ncsi_package *package,
848 struct ncsi_channel *disable,
849 struct ncsi_channel *enable)
850{
851 struct ncsi_cmd_arg nca;
852 struct ncsi_channel *nc;
853 struct ncsi_package *np;
854 int ret = 0;
855
856 if (!package->multi_channel && !ndp->multi_package)
857 netdev_warn(ndp->ndev.dev,
858 "NCSI: Trying to update Tx channel in single-channel mode\n");
859 nca.ndp = ndp;
860 nca.req_flags = 0;
861
862 /* Find current channel with Tx enabled */
863 NCSI_FOR_EACH_PACKAGE(ndp, np) {
864 if (disable)
865 break;
866 if (!ndp->multi_package && np != package)
867 continue;
868
869 NCSI_FOR_EACH_CHANNEL(np, nc)
870 if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
871 disable = nc;
872 break;
873 }
874 }
875
876 /* Find a suitable channel for Tx */
877 NCSI_FOR_EACH_PACKAGE(ndp, np) {
878 if (enable)
879 break;
880 if (!ndp->multi_package && np != package)
881 continue;
882 if (!(ndp->package_whitelist & (0x1 << np->id)))
883 continue;
884
885 if (np->preferred_channel &&
886 ncsi_channel_has_link(np->preferred_channel)) {
887 enable = np->preferred_channel;
888 break;
889 }
890
891 NCSI_FOR_EACH_CHANNEL(np, nc) {
892 if (!(np->channel_whitelist & 0x1 << nc->id))
893 continue;
894 if (nc->state != NCSI_CHANNEL_ACTIVE)
895 continue;
896 if (ncsi_channel_has_link(nc)) {
897 enable = nc;
898 break;
899 }
900 }
901 }
902
903 if (disable == enable)
904 return -1;
905
906 if (!enable)
907 return -1;
908
909 if (disable) {
910 nca.channel = disable->id;
911 nca.package = disable->package->id;
912 nca.type = NCSI_PKT_CMD_DCNT;
913 ret = ncsi_xmit_cmd(&nca);
914 if (ret)
915 netdev_err(ndp->ndev.dev,
916 "Error %d sending DCNT\n",
917 ret);
918 }
919
920 netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
921
922 nca.channel = enable->id;
923 nca.package = enable->package->id;
924 nca.type = NCSI_PKT_CMD_ECNT;
925 ret = ncsi_xmit_cmd(&nca);
926 if (ret)
927 netdev_err(ndp->ndev.dev,
928 "Error %d sending ECNT\n",
929 ret);
930
931 return ret;
932}
933
Gavin Shane6f44ed2016-07-19 11:54:19 +1000934static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
935{
Gavin Shane6f44ed2016-07-19 11:54:19 +1000936 struct ncsi_package *np = ndp->active_package;
937 struct ncsi_channel *nc = ndp->active_channel;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100938 struct ncsi_channel *hot_nc = NULL;
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +1100939 struct ncsi_dev *nd = &ndp->ndev;
940 struct net_device *dev = nd->dev;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000941 struct ncsi_cmd_arg nca;
942 unsigned char index;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100943 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000944 int ret;
945
946 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100947 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000948 switch (nd->state) {
949 case ncsi_dev_state_config:
950 case ncsi_dev_state_config_sp:
951 ndp->pending_req_num = 1;
952
953 /* Select the specific package */
954 nca.type = NCSI_PKT_CMD_SP;
955 if (ndp->flags & NCSI_DEV_HWA)
956 nca.bytes[0] = 0;
957 else
958 nca.bytes[0] = 1;
959 nca.package = np->id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +1100960 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000961 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100962 if (ret) {
963 netdev_err(ndp->ndev.dev,
964 "NCSI: Failed to transmit CMD_SP\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +1000965 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100966 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000967
968 nd->state = ncsi_dev_state_config_cis;
969 break;
970 case ncsi_dev_state_config_cis:
971 ndp->pending_req_num = 1;
972
973 /* Clear initial state */
974 nca.type = NCSI_PKT_CMD_CIS;
975 nca.package = np->id;
976 nca.channel = nc->id;
977 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100978 if (ret) {
979 netdev_err(ndp->ndev.dev,
980 "NCSI: Failed to transmit CMD_CIS\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +1000981 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100982 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000983
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700984 nd->state = ncsi_dev_state_config_oem_gma;
985 break;
986 case ncsi_dev_state_config_oem_gma:
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000987 nd->state = ncsi_dev_state_config_clear_vids;
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700988 ret = -1;
989
990#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
991 nca.type = NCSI_PKT_CMD_OEM;
992 nca.package = np->id;
993 nca.channel = nc->id;
994 ndp->pending_req_num = 1;
995 ret = ncsi_gma_handler(&nca, nc->version.mf_id);
996#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
997
998 if (ret < 0)
999 schedule_work(&ndp->work);
1000
Gavin Shane6f44ed2016-07-19 11:54:19 +10001001 break;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001002 case ncsi_dev_state_config_clear_vids:
1003 case ncsi_dev_state_config_svf:
1004 case ncsi_dev_state_config_ev:
Gavin Shane6f44ed2016-07-19 11:54:19 +10001005 case ncsi_dev_state_config_sma:
1006 case ncsi_dev_state_config_ebf:
Vijay Khemkacf0eba32019-09-12 12:04:50 -07001007 case ncsi_dev_state_config_dgmf:
Gavin Shane6f44ed2016-07-19 11:54:19 +10001008 case ncsi_dev_state_config_ecnt:
1009 case ncsi_dev_state_config_ec:
1010 case ncsi_dev_state_config_ae:
1011 case ncsi_dev_state_config_gls:
1012 ndp->pending_req_num = 1;
1013
1014 nca.package = np->id;
1015 nca.channel = nc->id;
1016
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001017 /* Clear any active filters on the channel before setting */
1018 if (nd->state == ncsi_dev_state_config_clear_vids) {
1019 ret = clear_one_vid(ndp, nc, &nca);
1020 if (ret) {
1021 nd->state = ncsi_dev_state_config_svf;
1022 schedule_work(&ndp->work);
1023 break;
1024 }
1025 /* Repeat */
1026 nd->state = ncsi_dev_state_config_clear_vids;
1027 /* Add known VLAN tags to the filter */
1028 } else if (nd->state == ncsi_dev_state_config_svf) {
1029 ret = set_one_vid(ndp, nc, &nca);
1030 if (ret) {
1031 nd->state = ncsi_dev_state_config_ev;
1032 schedule_work(&ndp->work);
1033 break;
1034 }
1035 /* Repeat */
1036 nd->state = ncsi_dev_state_config_svf;
1037 /* Enable/Disable the VLAN filter */
1038 } else if (nd->state == ncsi_dev_state_config_ev) {
1039 if (list_empty(&ndp->vlan_vids)) {
1040 nca.type = NCSI_PKT_CMD_DV;
1041 } else {
1042 nca.type = NCSI_PKT_CMD_EV;
1043 nca.bytes[3] = NCSI_CAP_VLAN_NO;
1044 }
1045 nd->state = ncsi_dev_state_config_sma;
1046 } else if (nd->state == ncsi_dev_state_config_sma) {
Gavin Shane6f44ed2016-07-19 11:54:19 +10001047 /* Use first entry in unicast filter table. Note that
1048 * the MAC filter table starts from entry 1 instead of
1049 * 0.
1050 */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001051 nca.type = NCSI_PKT_CMD_SMA;
1052 for (index = 0; index < 6; index++)
1053 nca.bytes[index] = dev->dev_addr[index];
1054 nca.bytes[6] = 0x1;
1055 nca.bytes[7] = 0x1;
1056 nd->state = ncsi_dev_state_config_ebf;
1057 } else if (nd->state == ncsi_dev_state_config_ebf) {
1058 nca.type = NCSI_PKT_CMD_EBF;
1059 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
Vijay Khemkacf0eba32019-09-12 12:04:50 -07001060 /* if multicast global filtering is supported then
1061 * disable it so that all multicast packet will be
1062 * forwarded to management controller
1063 */
1064 if (nc->caps[NCSI_CAP_GENERIC].cap &
1065 NCSI_CAP_GENERIC_MC)
1066 nd->state = ncsi_dev_state_config_dgmf;
1067 else if (ncsi_channel_is_tx(ndp, nc))
1068 nd->state = ncsi_dev_state_config_ecnt;
1069 else
1070 nd->state = ncsi_dev_state_config_ec;
1071 } else if (nd->state == ncsi_dev_state_config_dgmf) {
1072 nca.type = NCSI_PKT_CMD_DGMF;
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001073 if (ncsi_channel_is_tx(ndp, nc))
1074 nd->state = ncsi_dev_state_config_ecnt;
1075 else
1076 nd->state = ncsi_dev_state_config_ec;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001077 } else if (nd->state == ncsi_dev_state_config_ecnt) {
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001078 if (np->preferred_channel &&
1079 nc != np->preferred_channel)
1080 netdev_info(ndp->ndev.dev,
1081 "NCSI: Tx failed over to channel %u\n",
1082 nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001083 nca.type = NCSI_PKT_CMD_ECNT;
1084 nd->state = ncsi_dev_state_config_ec;
1085 } else if (nd->state == ncsi_dev_state_config_ec) {
1086 /* Enable AEN if it's supported */
1087 nca.type = NCSI_PKT_CMD_EC;
1088 nd->state = ncsi_dev_state_config_ae;
1089 if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
1090 nd->state = ncsi_dev_state_config_gls;
1091 } else if (nd->state == ncsi_dev_state_config_ae) {
1092 nca.type = NCSI_PKT_CMD_AE;
1093 nca.bytes[0] = 0;
1094 nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
1095 nd->state = ncsi_dev_state_config_gls;
1096 } else if (nd->state == ncsi_dev_state_config_gls) {
1097 nca.type = NCSI_PKT_CMD_GLS;
1098 nd->state = ncsi_dev_state_config_done;
1099 }
1100
1101 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001102 if (ret) {
1103 netdev_err(ndp->ndev.dev,
1104 "NCSI: Failed to transmit CMD %x\n",
1105 nca.type);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001106 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001107 }
Gavin Shane6f44ed2016-07-19 11:54:19 +10001108 break;
1109 case ncsi_dev_state_config_done:
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301110 netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
1111 nc->id);
Gavin Shand8cedaa2016-10-04 11:25:47 +11001112 spin_lock_irqsave(&nc->lock, flags);
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001113 nc->state = NCSI_CHANNEL_ACTIVE;
1114
1115 if (ndp->flags & NCSI_DEV_RESET) {
1116 /* A reset event happened during config, start it now */
1117 nc->reconfigure_needed = false;
1118 spin_unlock_irqrestore(&nc->lock, flags);
1119 ncsi_reset_dev(nd);
1120 break;
1121 }
1122
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001123 if (nc->reconfigure_needed) {
1124 /* This channel's configuration has been updated
1125 * part-way during the config state - start the
1126 * channel configuration over
1127 */
1128 nc->reconfigure_needed = false;
1129 nc->state = NCSI_CHANNEL_INACTIVE;
1130 spin_unlock_irqrestore(&nc->lock, flags);
1131
1132 spin_lock_irqsave(&ndp->lock, flags);
1133 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1134 spin_unlock_irqrestore(&ndp->lock, flags);
1135
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301136 netdev_dbg(dev, "Dirty NCSI channel state reset\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001137 ncsi_process_next_channel(ndp);
1138 break;
1139 }
1140
Gavin Shanbbc7c012016-10-20 11:45:51 +11001141 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
1142 hot_nc = nc;
Gavin Shanbbc7c012016-10-20 11:45:51 +11001143 } else {
1144 hot_nc = NULL;
Joel Stanley87975a02018-06-19 15:08:31 +09301145 netdev_dbg(ndp->ndev.dev,
1146 "NCSI: channel %u link down after config\n",
1147 nc->id);
Gavin Shanbbc7c012016-10-20 11:45:51 +11001148 }
Gavin Shand8cedaa2016-10-04 11:25:47 +11001149 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001150
Gavin Shanbbc7c012016-10-20 11:45:51 +11001151 /* Update the hot channel */
1152 spin_lock_irqsave(&ndp->lock, flags);
1153 ndp->hot_channel = hot_nc;
1154 spin_unlock_irqrestore(&ndp->lock, flags);
1155
Gavin Shane6f44ed2016-07-19 11:54:19 +10001156 ncsi_start_channel_monitor(nc);
1157 ncsi_process_next_channel(ndp);
1158 break;
1159 default:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001160 netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
1161 nd->state);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001162 }
1163
1164 return;
1165
1166error:
1167 ncsi_report_link(ndp, true);
1168}
1169
1170static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
1171{
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001172 struct ncsi_channel *nc, *found, *hot_nc;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001173 struct ncsi_channel_mode *ncm;
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001174 unsigned long flags, cflags;
1175 struct ncsi_package *np;
1176 bool with_link;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001177
Gavin Shanbbc7c012016-10-20 11:45:51 +11001178 spin_lock_irqsave(&ndp->lock, flags);
1179 hot_nc = ndp->hot_channel;
1180 spin_unlock_irqrestore(&ndp->lock, flags);
1181
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001182 /* By default the search is done once an inactive channel with up
1183 * link is found, unless a preferred channel is set.
1184 * If multi_package or multi_channel are configured all channels in the
1185 * whitelist are added to the channel queue.
Gavin Shane6f44ed2016-07-19 11:54:19 +10001186 */
1187 found = NULL;
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001188 with_link = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001189 NCSI_FOR_EACH_PACKAGE(ndp, np) {
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001190 if (!(ndp->package_whitelist & (0x1 << np->id)))
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +11001191 continue;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001192 NCSI_FOR_EACH_CHANNEL(np, nc) {
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001193 if (!(np->channel_whitelist & (0x1 << nc->id)))
1194 continue;
1195
1196 spin_lock_irqsave(&nc->lock, cflags);
Gavin Shand8cedaa2016-10-04 11:25:47 +11001197
Gavin Shane6f44ed2016-07-19 11:54:19 +10001198 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +11001199 nc->state != NCSI_CHANNEL_INACTIVE) {
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001200 spin_unlock_irqrestore(&nc->lock, cflags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001201 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +11001202 }
Gavin Shane6f44ed2016-07-19 11:54:19 +10001203
1204 if (!found)
1205 found = nc;
1206
Gavin Shanbbc7c012016-10-20 11:45:51 +11001207 if (nc == hot_nc)
1208 found = nc;
1209
Gavin Shane6f44ed2016-07-19 11:54:19 +10001210 ncm = &nc->modes[NCSI_MODE_LINK];
1211 if (ncm->data[2] & 0x1) {
1212 found = nc;
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001213 with_link = true;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001214 }
Gavin Shand8cedaa2016-10-04 11:25:47 +11001215
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001216 /* If multi_channel is enabled configure all valid
1217 * channels whether or not they currently have link
1218 * so they will have AENs enabled.
1219 */
1220 if (with_link || np->multi_channel) {
1221 spin_lock_irqsave(&ndp->lock, flags);
1222 list_add_tail_rcu(&nc->link,
1223 &ndp->channel_queue);
1224 spin_unlock_irqrestore(&ndp->lock, flags);
1225
1226 netdev_dbg(ndp->ndev.dev,
1227 "NCSI: Channel %u added to queue (link %s)\n",
1228 nc->id,
1229 ncm->data[2] & 0x1 ? "up" : "down");
1230 }
1231
1232 spin_unlock_irqrestore(&nc->lock, cflags);
1233
1234 if (with_link && !np->multi_channel)
1235 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001236 }
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001237 if (with_link && !ndp->multi_package)
1238 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001239 }
1240
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001241 if (list_empty(&ndp->channel_queue) && found) {
1242 netdev_info(ndp->ndev.dev,
1243 "NCSI: No channel with link found, configuring channel %u\n",
1244 found->id);
1245 spin_lock_irqsave(&ndp->lock, flags);
1246 list_add_tail_rcu(&found->link, &ndp->channel_queue);
1247 spin_unlock_irqrestore(&ndp->lock, flags);
1248 } else if (!found) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001249 netdev_warn(ndp->ndev.dev,
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001250 "NCSI: No channel found to configure!\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +10001251 ncsi_report_link(ndp, true);
1252 return -ENODEV;
1253 }
1254
Gavin Shane6f44ed2016-07-19 11:54:19 +10001255 return ncsi_process_next_channel(ndp);
1256}
1257
1258static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
1259{
1260 struct ncsi_package *np;
1261 struct ncsi_channel *nc;
1262 unsigned int cap;
Gavin Shan100ef012017-10-19 13:43:07 +11001263 bool has_channel = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001264
1265 /* The hardware arbitration is disabled if any one channel
1266 * doesn't support explicitly.
1267 */
1268 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1269 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shan100ef012017-10-19 13:43:07 +11001270 has_channel = true;
1271
Gavin Shane6f44ed2016-07-19 11:54:19 +10001272 cap = nc->caps[NCSI_CAP_GENERIC].cap;
1273 if (!(cap & NCSI_CAP_GENERIC_HWA) ||
1274 (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
1275 NCSI_CAP_GENERIC_HWA_SUPPORT) {
1276 ndp->flags &= ~NCSI_DEV_HWA;
1277 return false;
1278 }
1279 }
1280 }
1281
Gavin Shan100ef012017-10-19 13:43:07 +11001282 if (has_channel) {
1283 ndp->flags |= NCSI_DEV_HWA;
1284 return true;
1285 }
1286
1287 ndp->flags &= ~NCSI_DEV_HWA;
1288 return false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001289}
1290
Gavin Shane6f44ed2016-07-19 11:54:19 +10001291static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
1292{
1293 struct ncsi_dev *nd = &ndp->ndev;
1294 struct ncsi_package *np;
1295 struct ncsi_channel *nc;
1296 struct ncsi_cmd_arg nca;
1297 unsigned char index;
1298 int ret;
1299
1300 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +11001301 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001302 switch (nd->state) {
1303 case ncsi_dev_state_probe:
1304 nd->state = ncsi_dev_state_probe_deselect;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05001305 fallthrough;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001306 case ncsi_dev_state_probe_deselect:
1307 ndp->pending_req_num = 8;
1308
1309 /* Deselect all possible packages */
1310 nca.type = NCSI_PKT_CMD_DP;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001311 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001312 for (index = 0; index < 8; index++) {
1313 nca.package = index;
1314 ret = ncsi_xmit_cmd(&nca);
1315 if (ret)
1316 goto error;
1317 }
1318
1319 nd->state = ncsi_dev_state_probe_package;
1320 break;
1321 case ncsi_dev_state_probe_package:
Gavin Shane6f44ed2016-07-19 11:54:19 +10001322 ndp->pending_req_num = 1;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001323
Gavin Shane6f44ed2016-07-19 11:54:19 +10001324 nca.type = NCSI_PKT_CMD_SP;
1325 nca.bytes[0] = 1;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001326 nca.package = ndp->package_probe_id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001327 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001328 ret = ncsi_xmit_cmd(&nca);
1329 if (ret)
1330 goto error;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001331 nd->state = ncsi_dev_state_probe_channel;
1332 break;
1333 case ncsi_dev_state_probe_channel:
1334 ndp->active_package = ncsi_find_package(ndp,
1335 ndp->package_probe_id);
1336 if (!ndp->active_package) {
1337 /* No response */
1338 nd->state = ncsi_dev_state_probe_dp;
1339 schedule_work(&ndp->work);
1340 break;
1341 }
Gavin Shane6f44ed2016-07-19 11:54:19 +10001342 nd->state = ncsi_dev_state_probe_cis;
Vijay Khemka5e0fcc12020-01-08 15:43:40 -08001343 if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) &&
1344 ndp->mlx_multi_host)
1345 nd->state = ncsi_dev_state_probe_mlx_gma;
1346
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001347 schedule_work(&ndp->work);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001348 break;
Vijay Khemka5e0fcc12020-01-08 15:43:40 -08001349#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
1350 case ncsi_dev_state_probe_mlx_gma:
1351 ndp->pending_req_num = 1;
1352
1353 nca.type = NCSI_PKT_CMD_OEM;
1354 nca.package = ndp->active_package->id;
1355 nca.channel = 0;
1356 ret = ncsi_oem_gma_handler_mlx(&nca);
1357 if (ret)
1358 goto error;
1359
1360 nd->state = ncsi_dev_state_probe_mlx_smaf;
1361 break;
1362 case ncsi_dev_state_probe_mlx_smaf:
1363 ndp->pending_req_num = 1;
1364
1365 nca.type = NCSI_PKT_CMD_OEM;
1366 nca.package = ndp->active_package->id;
1367 nca.channel = 0;
1368 ret = ncsi_oem_smaf_mlx(&nca);
1369 if (ret)
1370 goto error;
1371
1372 nd->state = ncsi_dev_state_probe_cis;
1373 break;
1374#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001375 case ncsi_dev_state_probe_cis:
Gavin Shan55e02d02016-10-04 11:25:49 +11001376 ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001377
1378 /* Clear initial state */
1379 nca.type = NCSI_PKT_CMD_CIS;
1380 nca.package = ndp->active_package->id;
Gavin Shan55e02d02016-10-04 11:25:49 +11001381 for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
Gavin Shane6f44ed2016-07-19 11:54:19 +10001382 nca.channel = index;
1383 ret = ncsi_xmit_cmd(&nca);
1384 if (ret)
1385 goto error;
1386 }
1387
1388 nd->state = ncsi_dev_state_probe_gvi;
1389 break;
1390 case ncsi_dev_state_probe_gvi:
1391 case ncsi_dev_state_probe_gc:
1392 case ncsi_dev_state_probe_gls:
1393 np = ndp->active_package;
1394 ndp->pending_req_num = np->channel_num;
1395
1396 /* Retrieve version, capability or link status */
1397 if (nd->state == ncsi_dev_state_probe_gvi)
1398 nca.type = NCSI_PKT_CMD_GVI;
1399 else if (nd->state == ncsi_dev_state_probe_gc)
1400 nca.type = NCSI_PKT_CMD_GC;
1401 else
1402 nca.type = NCSI_PKT_CMD_GLS;
1403
1404 nca.package = np->id;
1405 NCSI_FOR_EACH_CHANNEL(np, nc) {
1406 nca.channel = nc->id;
1407 ret = ncsi_xmit_cmd(&nca);
1408 if (ret)
1409 goto error;
1410 }
1411
1412 if (nd->state == ncsi_dev_state_probe_gvi)
1413 nd->state = ncsi_dev_state_probe_gc;
1414 else if (nd->state == ncsi_dev_state_probe_gc)
1415 nd->state = ncsi_dev_state_probe_gls;
1416 else
1417 nd->state = ncsi_dev_state_probe_dp;
1418 break;
1419 case ncsi_dev_state_probe_dp:
1420 ndp->pending_req_num = 1;
1421
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001422 /* Deselect the current package */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001423 nca.type = NCSI_PKT_CMD_DP;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001424 nca.package = ndp->package_probe_id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001425 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001426 ret = ncsi_xmit_cmd(&nca);
1427 if (ret)
1428 goto error;
1429
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001430 /* Probe next package */
1431 ndp->package_probe_id++;
1432 if (ndp->package_probe_id >= 8) {
1433 /* Probe finished */
1434 ndp->flags |= NCSI_DEV_PROBED;
1435 break;
1436 }
1437 nd->state = ncsi_dev_state_probe_package;
1438 ndp->active_package = NULL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001439 break;
1440 default:
1441 netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
1442 nd->state);
1443 }
1444
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001445 if (ndp->flags & NCSI_DEV_PROBED) {
1446 /* Check if all packages have HWA support */
1447 ncsi_check_hwa(ndp);
1448 ncsi_choose_active_channel(ndp);
1449 }
1450
Gavin Shane6f44ed2016-07-19 11:54:19 +10001451 return;
1452error:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001453 netdev_err(ndp->ndev.dev,
1454 "NCSI: Failed to transmit cmd 0x%x during probe\n",
1455 nca.type);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001456 ncsi_report_link(ndp, true);
1457}
1458
1459static void ncsi_dev_work(struct work_struct *work)
1460{
1461 struct ncsi_dev_priv *ndp = container_of(work,
1462 struct ncsi_dev_priv, work);
1463 struct ncsi_dev *nd = &ndp->ndev;
1464
1465 switch (nd->state & ncsi_dev_state_major) {
1466 case ncsi_dev_state_probe:
1467 ncsi_probe_channel(ndp);
1468 break;
1469 case ncsi_dev_state_suspend:
1470 ncsi_suspend_channel(ndp);
1471 break;
1472 case ncsi_dev_state_config:
1473 ncsi_configure_channel(ndp);
1474 break;
1475 default:
1476 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
1477 nd->state);
1478 }
1479}
1480
1481int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
1482{
1483 struct ncsi_channel *nc;
1484 int old_state;
1485 unsigned long flags;
1486
1487 spin_lock_irqsave(&ndp->lock, flags);
1488 nc = list_first_or_null_rcu(&ndp->channel_queue,
1489 struct ncsi_channel, link);
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001490 if (!nc) {
1491 spin_unlock_irqrestore(&ndp->lock, flags);
1492 goto out;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001493 }
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001494
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001495 list_del_init(&nc->link);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001496 spin_unlock_irqrestore(&ndp->lock, flags);
1497
Gavin Shand8cedaa2016-10-04 11:25:47 +11001498 spin_lock_irqsave(&nc->lock, flags);
1499 old_state = nc->state;
1500 nc->state = NCSI_CHANNEL_INVISIBLE;
1501 spin_unlock_irqrestore(&nc->lock, flags);
1502
Gavin Shane6f44ed2016-07-19 11:54:19 +10001503 ndp->active_channel = nc;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001504 ndp->active_package = nc->package;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001505
1506 switch (old_state) {
1507 case NCSI_CHANNEL_INACTIVE:
1508 ndp->ndev.state = ncsi_dev_state_config;
Joel Stanley87975a02018-06-19 15:08:31 +09301509 netdev_dbg(ndp->ndev.dev, "NCSI: configuring channel %u\n",
1510 nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001511 ncsi_configure_channel(ndp);
1512 break;
1513 case NCSI_CHANNEL_ACTIVE:
1514 ndp->ndev.state = ncsi_dev_state_suspend;
Joel Stanley87975a02018-06-19 15:08:31 +09301515 netdev_dbg(ndp->ndev.dev, "NCSI: suspending channel %u\n",
1516 nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001517 ncsi_suspend_channel(ndp);
1518 break;
1519 default:
1520 netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
Gavin Shand8cedaa2016-10-04 11:25:47 +11001521 old_state, nc->package->id, nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001522 ncsi_report_link(ndp, false);
1523 return -EINVAL;
1524 }
1525
1526 return 0;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001527
1528out:
1529 ndp->active_channel = NULL;
1530 ndp->active_package = NULL;
1531 if (ndp->flags & NCSI_DEV_RESHUFFLE) {
1532 ndp->flags &= ~NCSI_DEV_RESHUFFLE;
1533 return ncsi_choose_active_channel(ndp);
1534 }
1535
1536 ncsi_report_link(ndp, false);
1537 return -ENODEV;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001538}
1539
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001540static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
1541{
1542 struct ncsi_dev *nd = &ndp->ndev;
1543 struct ncsi_channel *nc;
1544 struct ncsi_package *np;
1545 unsigned long flags;
1546 unsigned int n = 0;
1547
1548 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1549 NCSI_FOR_EACH_CHANNEL(np, nc) {
1550 spin_lock_irqsave(&nc->lock, flags);
1551
1552 /* Channels may be busy, mark dirty instead of
1553 * kicking if;
1554 * a) not ACTIVE (configured)
1555 * b) in the channel_queue (to be configured)
1556 * c) it's ndev is in the config state
1557 */
1558 if (nc->state != NCSI_CHANNEL_ACTIVE) {
1559 if ((ndp->ndev.state & 0xff00) ==
1560 ncsi_dev_state_config ||
1561 !list_empty(&nc->link)) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301562 netdev_dbg(nd->dev,
1563 "NCSI: channel %p marked dirty\n",
1564 nc);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001565 nc->reconfigure_needed = true;
1566 }
1567 spin_unlock_irqrestore(&nc->lock, flags);
1568 continue;
1569 }
1570
1571 spin_unlock_irqrestore(&nc->lock, flags);
1572
1573 ncsi_stop_channel_monitor(nc);
1574 spin_lock_irqsave(&nc->lock, flags);
1575 nc->state = NCSI_CHANNEL_INACTIVE;
1576 spin_unlock_irqrestore(&nc->lock, flags);
1577
1578 spin_lock_irqsave(&ndp->lock, flags);
1579 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1580 spin_unlock_irqrestore(&ndp->lock, flags);
1581
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301582 netdev_dbg(nd->dev, "NCSI: kicked channel %p\n", nc);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001583 n++;
1584 }
1585 }
1586
1587 return n;
1588}
1589
1590int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
1591{
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001592 struct ncsi_dev_priv *ndp;
1593 unsigned int n_vids = 0;
1594 struct vlan_vid *vlan;
1595 struct ncsi_dev *nd;
1596 bool found = false;
1597
1598 if (vid == 0)
1599 return 0;
1600
1601 nd = ncsi_find_dev(dev);
1602 if (!nd) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001603 netdev_warn(dev, "NCSI: No net_device?\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001604 return 0;
1605 }
1606
1607 ndp = TO_NCSI_DEV_PRIV(nd);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001608
1609 /* Add the VLAN id to our internal list */
1610 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
1611 n_vids++;
1612 if (vlan->vid == vid) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301613 netdev_dbg(dev, "NCSI: vid %u already registered\n",
1614 vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001615 return 0;
1616 }
1617 }
Samuel Mendoza-Jonas6e9c0072017-10-11 16:54:27 +11001618 if (n_vids >= NCSI_MAX_VLAN_VIDS) {
1619 netdev_warn(dev,
1620 "tried to add vlan id %u but NCSI max already registered (%u)\n",
1621 vid, NCSI_MAX_VLAN_VIDS);
1622 return -ENOSPC;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001623 }
1624
1625 vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
1626 if (!vlan)
1627 return -ENOMEM;
1628
1629 vlan->proto = proto;
1630 vlan->vid = vid;
1631 list_add_rcu(&vlan->list, &ndp->vlan_vids);
1632
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301633 netdev_dbg(dev, "NCSI: Added new vid %u\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001634
1635 found = ncsi_kick_channels(ndp) != 0;
1636
1637 return found ? ncsi_process_next_channel(ndp) : 0;
1638}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001639EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001640
1641int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
1642{
1643 struct vlan_vid *vlan, *tmp;
1644 struct ncsi_dev_priv *ndp;
1645 struct ncsi_dev *nd;
1646 bool found = false;
1647
1648 if (vid == 0)
1649 return 0;
1650
1651 nd = ncsi_find_dev(dev);
1652 if (!nd) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001653 netdev_warn(dev, "NCSI: no net_device?\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001654 return 0;
1655 }
1656
1657 ndp = TO_NCSI_DEV_PRIV(nd);
1658
1659 /* Remove the VLAN id from our internal list */
1660 list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
1661 if (vlan->vid == vid) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301662 netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001663 list_del_rcu(&vlan->list);
1664 found = true;
1665 kfree(vlan);
1666 }
1667
1668 if (!found) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001669 netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001670 return -EINVAL;
1671 }
1672
1673 found = ncsi_kick_channels(ndp) != 0;
1674
1675 return found ? ncsi_process_next_channel(ndp) : 0;
1676}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001677EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001678
Gavin Shan2d283bd2016-07-19 11:54:16 +10001679struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
1680 void (*handler)(struct ncsi_dev *ndev))
1681{
1682 struct ncsi_dev_priv *ndp;
1683 struct ncsi_dev *nd;
Vijay Khemka5e0fcc12020-01-08 15:43:40 -08001684 struct platform_device *pdev;
1685 struct device_node *np;
Gavin Shan2d283bd2016-07-19 11:54:16 +10001686 unsigned long flags;
1687 int i;
1688
1689 /* Check if the device has been registered or not */
1690 nd = ncsi_find_dev(dev);
1691 if (nd)
1692 return nd;
1693
1694 /* Create NCSI device */
1695 ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
1696 if (!ndp)
1697 return NULL;
1698
1699 nd = &ndp->ndev;
1700 nd->state = ncsi_dev_state_registered;
1701 nd->dev = dev;
1702 nd->handler = handler;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001703 ndp->pending_req_num = 0;
1704 INIT_LIST_HEAD(&ndp->channel_queue);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001705 INIT_LIST_HEAD(&ndp->vlan_vids);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001706 INIT_WORK(&ndp->work, ncsi_dev_work);
Samuel Mendoza-Jonas8d951a72018-11-16 15:51:59 +11001707 ndp->package_whitelist = UINT_MAX;
Gavin Shan2d283bd2016-07-19 11:54:16 +10001708
1709 /* Initialize private NCSI device */
1710 spin_lock_init(&ndp->lock);
1711 INIT_LIST_HEAD(&ndp->packages);
Gavin Shana15af542016-10-04 11:25:50 +11001712 ndp->request_id = NCSI_REQ_START_IDX;
Gavin Shan2d283bd2016-07-19 11:54:16 +10001713 for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
1714 ndp->requests[i].id = i;
1715 ndp->requests[i].ndp = ndp;
Kees Cooke99e88a2017-10-16 14:43:17 -07001716 timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0);
Gavin Shan2d283bd2016-07-19 11:54:16 +10001717 }
1718
1719 spin_lock_irqsave(&ncsi_dev_lock, flags);
1720 list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
1721 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1722
Gavin Shane6f44ed2016-07-19 11:54:19 +10001723 /* Register NCSI packet Rx handler */
1724 ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
1725 ndp->ptype.func = ncsi_rcv_rsp;
1726 ndp->ptype.dev = dev;
1727 dev_add_pack(&ndp->ptype);
1728
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +11001729 /* Set up generic netlink interface */
1730 ncsi_init_netlink(dev);
1731
Vijay Khemka5e0fcc12020-01-08 15:43:40 -08001732 pdev = to_platform_device(dev->dev.parent);
1733 if (pdev) {
1734 np = pdev->dev.of_node;
1735 if (np && of_get_property(np, "mlx,multi-host", NULL))
1736 ndp->mlx_multi_host = true;
1737 }
1738
Gavin Shan2d283bd2016-07-19 11:54:16 +10001739 return nd;
1740}
1741EXPORT_SYMBOL_GPL(ncsi_register_dev);
1742
Gavin Shane6f44ed2016-07-19 11:54:19 +10001743int ncsi_start_dev(struct ncsi_dev *nd)
1744{
1745 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001746
1747 if (nd->state != ncsi_dev_state_registered &&
1748 nd->state != ncsi_dev_state_functional)
1749 return -ENOTTY;
1750
1751 if (!(ndp->flags & NCSI_DEV_PROBED)) {
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001752 ndp->package_probe_id = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001753 nd->state = ncsi_dev_state_probe;
1754 schedule_work(&ndp->work);
1755 return 0;
1756 }
1757
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001758 return ncsi_reset_dev(nd);
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001759}
1760EXPORT_SYMBOL_GPL(ncsi_start_dev);
1761
1762void ncsi_stop_dev(struct ncsi_dev *nd)
1763{
1764 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1765 struct ncsi_package *np;
1766 struct ncsi_channel *nc;
1767 bool chained;
1768 int old_state;
1769 unsigned long flags;
1770
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001771 /* Stop the channel monitor on any active channels. Don't reset the
1772 * channel state so we know which were active when ncsi_start_dev()
1773 * is next called.
1774 */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001775 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1776 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001777 ncsi_stop_channel_monitor(nc);
1778
Gavin Shand8cedaa2016-10-04 11:25:47 +11001779 spin_lock_irqsave(&nc->lock, flags);
1780 chained = !list_empty(&nc->link);
1781 old_state = nc->state;
Gavin Shand8cedaa2016-10-04 11:25:47 +11001782 spin_unlock_irqrestore(&nc->lock, flags);
1783
1784 WARN_ON_ONCE(chained ||
Gavin Shane6f44ed2016-07-19 11:54:19 +10001785 old_state == NCSI_CHANNEL_INVISIBLE);
1786 }
1787 }
1788
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301789 netdev_dbg(ndp->ndev.dev, "NCSI: Stopping device\n");
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001790 ncsi_report_link(ndp, true);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001791}
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001792EXPORT_SYMBOL_GPL(ncsi_stop_dev);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001793
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001794int ncsi_reset_dev(struct ncsi_dev *nd)
1795{
1796 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1797 struct ncsi_channel *nc, *active, *tmp;
1798 struct ncsi_package *np;
1799 unsigned long flags;
1800
1801 spin_lock_irqsave(&ndp->lock, flags);
1802
1803 if (!(ndp->flags & NCSI_DEV_RESET)) {
1804 /* Haven't been called yet, check states */
1805 switch (nd->state & ncsi_dev_state_major) {
1806 case ncsi_dev_state_registered:
1807 case ncsi_dev_state_probe:
1808 /* Not even probed yet - do nothing */
1809 spin_unlock_irqrestore(&ndp->lock, flags);
1810 return 0;
1811 case ncsi_dev_state_suspend:
1812 case ncsi_dev_state_config:
1813 /* Wait for the channel to finish its suspend/config
1814 * operation; once it finishes it will check for
1815 * NCSI_DEV_RESET and reset the state.
1816 */
1817 ndp->flags |= NCSI_DEV_RESET;
1818 spin_unlock_irqrestore(&ndp->lock, flags);
1819 return 0;
1820 }
1821 } else {
1822 switch (nd->state) {
1823 case ncsi_dev_state_suspend_done:
1824 case ncsi_dev_state_config_done:
1825 case ncsi_dev_state_functional:
1826 /* Ok */
1827 break;
1828 default:
1829 /* Current reset operation happening */
1830 spin_unlock_irqrestore(&ndp->lock, flags);
1831 return 0;
1832 }
1833 }
1834
1835 if (!list_empty(&ndp->channel_queue)) {
1836 /* Clear any channel queue we may have interrupted */
1837 list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
1838 list_del_init(&nc->link);
1839 }
1840 spin_unlock_irqrestore(&ndp->lock, flags);
1841
1842 active = NULL;
1843 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1844 NCSI_FOR_EACH_CHANNEL(np, nc) {
1845 spin_lock_irqsave(&nc->lock, flags);
1846
1847 if (nc->state == NCSI_CHANNEL_ACTIVE) {
1848 active = nc;
1849 nc->state = NCSI_CHANNEL_INVISIBLE;
1850 spin_unlock_irqrestore(&nc->lock, flags);
1851 ncsi_stop_channel_monitor(nc);
1852 break;
1853 }
1854
1855 spin_unlock_irqrestore(&nc->lock, flags);
1856 }
1857 if (active)
1858 break;
1859 }
1860
1861 if (!active) {
1862 /* Done */
1863 spin_lock_irqsave(&ndp->lock, flags);
1864 ndp->flags &= ~NCSI_DEV_RESET;
1865 spin_unlock_irqrestore(&ndp->lock, flags);
1866 return ncsi_choose_active_channel(ndp);
1867 }
1868
1869 spin_lock_irqsave(&ndp->lock, flags);
1870 ndp->flags |= NCSI_DEV_RESET;
1871 ndp->active_channel = active;
1872 ndp->active_package = active->package;
1873 spin_unlock_irqrestore(&ndp->lock, flags);
1874
1875 nd->state = ncsi_dev_state_suspend;
1876 schedule_work(&ndp->work);
1877 return 0;
1878}
1879
Gavin Shan2d283bd2016-07-19 11:54:16 +10001880void ncsi_unregister_dev(struct ncsi_dev *nd)
1881{
1882 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1883 struct ncsi_package *np, *tmp;
1884 unsigned long flags;
1885
Gavin Shane6f44ed2016-07-19 11:54:19 +10001886 dev_remove_pack(&ndp->ptype);
1887
Gavin Shan2d283bd2016-07-19 11:54:16 +10001888 list_for_each_entry_safe(np, tmp, &ndp->packages, node)
1889 ncsi_remove_package(np);
1890
1891 spin_lock_irqsave(&ncsi_dev_lock, flags);
1892 list_del_rcu(&ndp->node);
Gavin Shan2d283bd2016-07-19 11:54:16 +10001893 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1894
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +11001895 ncsi_unregister_netlink(nd->dev);
1896
Gavin Shan2d283bd2016-07-19 11:54:16 +10001897 kfree(ndp);
1898}
1899EXPORT_SYMBOL_GPL(ncsi_unregister_dev);