12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22d283bddSGavin Shan /*
32d283bddSGavin Shan * Copyright Gavin Shan, IBM Corporation 2016.
42d283bddSGavin Shan */
52d283bddSGavin Shan
62d283bddSGavin Shan #include <linux/module.h>
72d283bddSGavin Shan #include <linux/kernel.h>
82d283bddSGavin Shan #include <linux/init.h>
92d283bddSGavin Shan #include <linux/netdevice.h>
102d283bddSGavin Shan #include <linux/skbuff.h>
115e0fcc16SVijay Khemka #include <linux/of.h>
125e0fcc16SVijay Khemka #include <linux/platform_device.h>
132d283bddSGavin Shan
142d283bddSGavin Shan #include <net/ncsi.h>
152d283bddSGavin Shan #include <net/net_namespace.h>
162d283bddSGavin Shan #include <net/sock.h>
17e6f44ed6SGavin Shan #include <net/addrconf.h>
18e6f44ed6SGavin Shan #include <net/ipv6.h>
199771b8ccSJustin.Lee1@Dell.com #include <net/genetlink.h>
202d283bddSGavin Shan
212d283bddSGavin Shan #include "internal.h"
22e6f44ed6SGavin Shan #include "ncsi-pkt.h"
23955dc68cSSamuel Mendoza-Jonas #include "ncsi-netlink.h"
242d283bddSGavin Shan
252d283bddSGavin Shan LIST_HEAD(ncsi_dev_list);
262d283bddSGavin Shan DEFINE_SPINLOCK(ncsi_dev_lock);
272d283bddSGavin Shan
ncsi_channel_has_link(struct ncsi_channel * channel)288d951a75SSamuel Mendoza-Jonas bool ncsi_channel_has_link(struct ncsi_channel *channel)
298d951a75SSamuel Mendoza-Jonas {
308d951a75SSamuel Mendoza-Jonas return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
318d951a75SSamuel Mendoza-Jonas }
328d951a75SSamuel Mendoza-Jonas
ncsi_channel_is_last(struct ncsi_dev_priv * ndp,struct ncsi_channel * channel)338d951a75SSamuel Mendoza-Jonas bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
348d951a75SSamuel Mendoza-Jonas struct ncsi_channel *channel)
358d951a75SSamuel Mendoza-Jonas {
368d951a75SSamuel Mendoza-Jonas struct ncsi_package *np;
378d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc;
388d951a75SSamuel Mendoza-Jonas
398d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np)
408d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) {
418d951a75SSamuel Mendoza-Jonas if (nc == channel)
428d951a75SSamuel Mendoza-Jonas continue;
438d951a75SSamuel Mendoza-Jonas if (nc->state == NCSI_CHANNEL_ACTIVE &&
448d951a75SSamuel Mendoza-Jonas ncsi_channel_has_link(nc))
458d951a75SSamuel Mendoza-Jonas return false;
468d951a75SSamuel Mendoza-Jonas }
478d951a75SSamuel Mendoza-Jonas
488d951a75SSamuel Mendoza-Jonas return true;
498d951a75SSamuel Mendoza-Jonas }
508d951a75SSamuel Mendoza-Jonas
ncsi_report_link(struct ncsi_dev_priv * ndp,bool force_down)51e6f44ed6SGavin Shan static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
52e6f44ed6SGavin Shan {
53e6f44ed6SGavin Shan struct ncsi_dev *nd = &ndp->ndev;
54e6f44ed6SGavin Shan struct ncsi_package *np;
55e6f44ed6SGavin Shan struct ncsi_channel *nc;
56d8cedaabSGavin Shan unsigned long flags;
57e6f44ed6SGavin Shan
58e6f44ed6SGavin Shan nd->state = ncsi_dev_state_functional;
59e6f44ed6SGavin Shan if (force_down) {
60e6f44ed6SGavin Shan nd->link_up = 0;
61e6f44ed6SGavin Shan goto report;
62e6f44ed6SGavin Shan }
63e6f44ed6SGavin Shan
64e6f44ed6SGavin Shan nd->link_up = 0;
65e6f44ed6SGavin Shan NCSI_FOR_EACH_PACKAGE(ndp, np) {
66e6f44ed6SGavin Shan NCSI_FOR_EACH_CHANNEL(np, nc) {
67d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
68d8cedaabSGavin Shan
69e6f44ed6SGavin Shan if (!list_empty(&nc->link) ||
70d8cedaabSGavin Shan nc->state != NCSI_CHANNEL_ACTIVE) {
71d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
72e6f44ed6SGavin Shan continue;
73d8cedaabSGavin Shan }
74e6f44ed6SGavin Shan
758d951a75SSamuel Mendoza-Jonas if (ncsi_channel_has_link(nc)) {
76d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
77e6f44ed6SGavin Shan nd->link_up = 1;
78e6f44ed6SGavin Shan goto report;
79e6f44ed6SGavin Shan }
80d8cedaabSGavin Shan
81d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
82e6f44ed6SGavin Shan }
83e6f44ed6SGavin Shan }
84e6f44ed6SGavin Shan
85e6f44ed6SGavin Shan report:
86e6f44ed6SGavin Shan nd->handler(nd);
87e6f44ed6SGavin Shan }
88e6f44ed6SGavin Shan
ncsi_channel_monitor(struct timer_list * t)8986cb30ecSKees Cook static void ncsi_channel_monitor(struct timer_list *t)
90e6f44ed6SGavin Shan {
9186cb30ecSKees Cook struct ncsi_channel *nc = from_timer(nc, t, monitor.timer);
92e6f44ed6SGavin Shan struct ncsi_package *np = nc->package;
93e6f44ed6SGavin Shan struct ncsi_dev_priv *ndp = np->ndp;
9452b4c862SGavin Shan struct ncsi_channel_mode *ncm;
95e6f44ed6SGavin Shan struct ncsi_cmd_arg nca;
96d8cedaabSGavin Shan bool enabled, chained;
9783afdc6aSGavin Shan unsigned int monitor_state;
98e6f44ed6SGavin Shan unsigned long flags;
99d8cedaabSGavin Shan int state, ret;
100e6f44ed6SGavin Shan
101e6f44ed6SGavin Shan spin_lock_irqsave(&nc->lock, flags);
102d8cedaabSGavin Shan state = nc->state;
103d8cedaabSGavin Shan chained = !list_empty(&nc->link);
10483afdc6aSGavin Shan enabled = nc->monitor.enabled;
10583afdc6aSGavin Shan monitor_state = nc->monitor.state;
106e6f44ed6SGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
107e6f44ed6SGavin Shan
10803cb4d05SMilton Miller if (!enabled)
10903cb4d05SMilton Miller return; /* expected race disabling timer */
11003cb4d05SMilton Miller if (WARN_ON_ONCE(chained))
11103cb4d05SMilton Miller goto bad_state;
11203cb4d05SMilton Miller
113d8cedaabSGavin Shan if (state != NCSI_CHANNEL_INACTIVE &&
1140795fb20SSamuel Mendoza-Jonas state != NCSI_CHANNEL_ACTIVE) {
11503cb4d05SMilton Miller bad_state:
11603cb4d05SMilton Miller netdev_warn(ndp->ndev.dev,
11703cb4d05SMilton Miller "Bad NCSI monitor state channel %d 0x%x %s queue\n",
11803cb4d05SMilton Miller nc->id, state, chained ? "on" : "off");
11903cb4d05SMilton Miller spin_lock_irqsave(&nc->lock, flags);
12003cb4d05SMilton Miller nc->monitor.enabled = false;
12103cb4d05SMilton Miller spin_unlock_irqrestore(&nc->lock, flags);
122e6f44ed6SGavin Shan return;
1230795fb20SSamuel Mendoza-Jonas }
124e6f44ed6SGavin Shan
12583afdc6aSGavin Shan switch (monitor_state) {
12683afdc6aSGavin Shan case NCSI_CHANNEL_MONITOR_START:
12783afdc6aSGavin Shan case NCSI_CHANNEL_MONITOR_RETRY:
128e6f44ed6SGavin Shan nca.ndp = ndp;
129e6f44ed6SGavin Shan nca.package = np->id;
130e6f44ed6SGavin Shan nca.channel = nc->id;
131e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_GLS;
132a0509cbeSGavin Shan nca.req_flags = 0;
133e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
1340795fb20SSamuel Mendoza-Jonas if (ret)
135e6f44ed6SGavin Shan netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
136e6f44ed6SGavin Shan ret);
13783afdc6aSGavin Shan break;
13883afdc6aSGavin Shan case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
13983afdc6aSGavin Shan break;
14083afdc6aSGavin Shan default:
1419ef8690bSSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
1429ef8690bSSamuel Mendoza-Jonas nc->id);
143e6f44ed6SGavin Shan ncsi_report_link(ndp, true);
14483afdc6aSGavin Shan ndp->flags |= NCSI_DEV_RESHUFFLE;
145e6f44ed6SGavin Shan
14652b4c862SGavin Shan ncm = &nc->modes[NCSI_MODE_LINK];
147d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
14803cb4d05SMilton Miller nc->monitor.enabled = false;
149d8cedaabSGavin Shan nc->state = NCSI_CHANNEL_INVISIBLE;
15052b4c862SGavin Shan ncm->data[2] &= ~0x1;
151d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
152d8cedaabSGavin Shan
153e6f44ed6SGavin Shan spin_lock_irqsave(&ndp->lock, flags);
15452b4c862SGavin Shan nc->state = NCSI_CHANNEL_ACTIVE;
155e6f44ed6SGavin Shan list_add_tail_rcu(&nc->link, &ndp->channel_queue);
156e6f44ed6SGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
157e6f44ed6SGavin Shan ncsi_process_next_channel(ndp);
158e6f44ed6SGavin Shan return;
159e6f44ed6SGavin Shan }
160e6f44ed6SGavin Shan
161e6f44ed6SGavin Shan spin_lock_irqsave(&nc->lock, flags);
16283afdc6aSGavin Shan nc->monitor.state++;
163e6f44ed6SGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
16483afdc6aSGavin Shan mod_timer(&nc->monitor.timer, jiffies + HZ);
165e6f44ed6SGavin Shan }
166e6f44ed6SGavin Shan
ncsi_start_channel_monitor(struct ncsi_channel * nc)167e6f44ed6SGavin Shan void ncsi_start_channel_monitor(struct ncsi_channel *nc)
168e6f44ed6SGavin Shan {
169e6f44ed6SGavin Shan unsigned long flags;
170e6f44ed6SGavin Shan
171e6f44ed6SGavin Shan spin_lock_irqsave(&nc->lock, flags);
17283afdc6aSGavin Shan WARN_ON_ONCE(nc->monitor.enabled);
17383afdc6aSGavin Shan nc->monitor.enabled = true;
17483afdc6aSGavin Shan nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
175e6f44ed6SGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
176e6f44ed6SGavin Shan
17783afdc6aSGavin Shan mod_timer(&nc->monitor.timer, jiffies + HZ);
178e6f44ed6SGavin Shan }
179e6f44ed6SGavin Shan
ncsi_stop_channel_monitor(struct ncsi_channel * nc)180e6f44ed6SGavin Shan void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
181e6f44ed6SGavin Shan {
182e6f44ed6SGavin Shan unsigned long flags;
183e6f44ed6SGavin Shan
184e6f44ed6SGavin Shan spin_lock_irqsave(&nc->lock, flags);
18583afdc6aSGavin Shan if (!nc->monitor.enabled) {
186e6f44ed6SGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
187e6f44ed6SGavin Shan return;
188e6f44ed6SGavin Shan }
18983afdc6aSGavin Shan nc->monitor.enabled = false;
190e6f44ed6SGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
191e6f44ed6SGavin Shan
19283afdc6aSGavin Shan del_timer_sync(&nc->monitor.timer);
193e6f44ed6SGavin Shan }
194e6f44ed6SGavin Shan
ncsi_find_channel(struct ncsi_package * np,unsigned char id)1952d283bddSGavin Shan struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
1962d283bddSGavin Shan unsigned char id)
1972d283bddSGavin Shan {
1982d283bddSGavin Shan struct ncsi_channel *nc;
1992d283bddSGavin Shan
2002d283bddSGavin Shan NCSI_FOR_EACH_CHANNEL(np, nc) {
2012d283bddSGavin Shan if (nc->id == id)
2022d283bddSGavin Shan return nc;
2032d283bddSGavin Shan }
2042d283bddSGavin Shan
2052d283bddSGavin Shan return NULL;
2062d283bddSGavin Shan }
2072d283bddSGavin Shan
ncsi_add_channel(struct ncsi_package * np,unsigned char id)2082d283bddSGavin Shan struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
2092d283bddSGavin Shan {
2102d283bddSGavin Shan struct ncsi_channel *nc, *tmp;
2112d283bddSGavin Shan int index;
2122d283bddSGavin Shan unsigned long flags;
2132d283bddSGavin Shan
2142d283bddSGavin Shan nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
2152d283bddSGavin Shan if (!nc)
2162d283bddSGavin Shan return NULL;
2172d283bddSGavin Shan
2182d283bddSGavin Shan nc->id = id;
2192d283bddSGavin Shan nc->package = np;
2202d283bddSGavin Shan nc->state = NCSI_CHANNEL_INACTIVE;
22183afdc6aSGavin Shan nc->monitor.enabled = false;
22286cb30ecSKees Cook timer_setup(&nc->monitor.timer, ncsi_channel_monitor, 0);
2232d283bddSGavin Shan spin_lock_init(&nc->lock);
224e6f44ed6SGavin Shan INIT_LIST_HEAD(&nc->link);
2252d283bddSGavin Shan for (index = 0; index < NCSI_CAP_MAX; index++)
2262d283bddSGavin Shan nc->caps[index].index = index;
2272d283bddSGavin Shan for (index = 0; index < NCSI_MODE_MAX; index++)
2282d283bddSGavin Shan nc->modes[index].index = index;
2292d283bddSGavin Shan
2302d283bddSGavin Shan spin_lock_irqsave(&np->lock, flags);
2312d283bddSGavin Shan tmp = ncsi_find_channel(np, id);
2322d283bddSGavin Shan if (tmp) {
2332d283bddSGavin Shan spin_unlock_irqrestore(&np->lock, flags);
2342d283bddSGavin Shan kfree(nc);
2352d283bddSGavin Shan return tmp;
2362d283bddSGavin Shan }
2372d283bddSGavin Shan
2382d283bddSGavin Shan list_add_tail_rcu(&nc->node, &np->channels);
2392d283bddSGavin Shan np->channel_num++;
2402d283bddSGavin Shan spin_unlock_irqrestore(&np->lock, flags);
2412d283bddSGavin Shan
2422d283bddSGavin Shan return nc;
2432d283bddSGavin Shan }
2442d283bddSGavin Shan
ncsi_remove_channel(struct ncsi_channel * nc)2452d283bddSGavin Shan static void ncsi_remove_channel(struct ncsi_channel *nc)
2462d283bddSGavin Shan {
2472d283bddSGavin Shan struct ncsi_package *np = nc->package;
2482d283bddSGavin Shan unsigned long flags;
249062b3e1bSSamuel Mendoza-Jonas
250062b3e1bSSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
2512d283bddSGavin Shan
2522d283bddSGavin Shan /* Release filters */
253062b3e1bSSamuel Mendoza-Jonas kfree(nc->mac_filter.addrs);
254062b3e1bSSamuel Mendoza-Jonas kfree(nc->vlan_filter.vids);
2552d283bddSGavin Shan
2562d283bddSGavin Shan nc->state = NCSI_CHANNEL_INACTIVE;
2572d283bddSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
258e6f44ed6SGavin Shan ncsi_stop_channel_monitor(nc);
2592d283bddSGavin Shan
2602d283bddSGavin Shan /* Remove and free channel */
2612d283bddSGavin Shan spin_lock_irqsave(&np->lock, flags);
2622d283bddSGavin Shan list_del_rcu(&nc->node);
2632d283bddSGavin Shan np->channel_num--;
2642d283bddSGavin Shan spin_unlock_irqrestore(&np->lock, flags);
2652d283bddSGavin Shan
2662d283bddSGavin Shan kfree(nc);
2672d283bddSGavin Shan }
2682d283bddSGavin Shan
ncsi_find_package(struct ncsi_dev_priv * ndp,unsigned char id)2692d283bddSGavin Shan struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
2702d283bddSGavin Shan unsigned char id)
2712d283bddSGavin Shan {
2722d283bddSGavin Shan struct ncsi_package *np;
2732d283bddSGavin Shan
2742d283bddSGavin Shan NCSI_FOR_EACH_PACKAGE(ndp, np) {
2752d283bddSGavin Shan if (np->id == id)
2762d283bddSGavin Shan return np;
2772d283bddSGavin Shan }
2782d283bddSGavin Shan
2792d283bddSGavin Shan return NULL;
2802d283bddSGavin Shan }
2812d283bddSGavin Shan
ncsi_add_package(struct ncsi_dev_priv * ndp,unsigned char id)2822d283bddSGavin Shan struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
2832d283bddSGavin Shan unsigned char id)
2842d283bddSGavin Shan {
2852d283bddSGavin Shan struct ncsi_package *np, *tmp;
2862d283bddSGavin Shan unsigned long flags;
2872d283bddSGavin Shan
2882d283bddSGavin Shan np = kzalloc(sizeof(*np), GFP_ATOMIC);
2892d283bddSGavin Shan if (!np)
2902d283bddSGavin Shan return NULL;
2912d283bddSGavin Shan
2922d283bddSGavin Shan np->id = id;
2932d283bddSGavin Shan np->ndp = ndp;
2942d283bddSGavin Shan spin_lock_init(&np->lock);
2952d283bddSGavin Shan INIT_LIST_HEAD(&np->channels);
2968d951a75SSamuel Mendoza-Jonas np->channel_whitelist = UINT_MAX;
2972d283bddSGavin Shan
2982d283bddSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
2992d283bddSGavin Shan tmp = ncsi_find_package(ndp, id);
3002d283bddSGavin Shan if (tmp) {
3012d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
3022d283bddSGavin Shan kfree(np);
3032d283bddSGavin Shan return tmp;
3042d283bddSGavin Shan }
3052d283bddSGavin Shan
3062d283bddSGavin Shan list_add_tail_rcu(&np->node, &ndp->packages);
3072d283bddSGavin Shan ndp->package_num++;
3082d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
3092d283bddSGavin Shan
3102d283bddSGavin Shan return np;
3112d283bddSGavin Shan }
3122d283bddSGavin Shan
ncsi_remove_package(struct ncsi_package * np)3132d283bddSGavin Shan void ncsi_remove_package(struct ncsi_package *np)
3142d283bddSGavin Shan {
3152d283bddSGavin Shan struct ncsi_dev_priv *ndp = np->ndp;
3162d283bddSGavin Shan struct ncsi_channel *nc, *tmp;
3172d283bddSGavin Shan unsigned long flags;
3182d283bddSGavin Shan
3192d283bddSGavin Shan /* Release all child channels */
3202d283bddSGavin Shan list_for_each_entry_safe(nc, tmp, &np->channels, node)
3212d283bddSGavin Shan ncsi_remove_channel(nc);
3222d283bddSGavin Shan
3232d283bddSGavin Shan /* Remove and free package */
3242d283bddSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
3252d283bddSGavin Shan list_del_rcu(&np->node);
3262d283bddSGavin Shan ndp->package_num--;
3272d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
3282d283bddSGavin Shan
3292d283bddSGavin Shan kfree(np);
3302d283bddSGavin Shan }
3312d283bddSGavin Shan
ncsi_find_package_and_channel(struct ncsi_dev_priv * ndp,unsigned char id,struct ncsi_package ** np,struct ncsi_channel ** nc)3322d283bddSGavin Shan void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
3332d283bddSGavin Shan unsigned char id,
3342d283bddSGavin Shan struct ncsi_package **np,
3352d283bddSGavin Shan struct ncsi_channel **nc)
3362d283bddSGavin Shan {
3372d283bddSGavin Shan struct ncsi_package *p;
3382d283bddSGavin Shan struct ncsi_channel *c;
3392d283bddSGavin Shan
3402d283bddSGavin Shan p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
3412d283bddSGavin Shan c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
3422d283bddSGavin Shan
3432d283bddSGavin Shan if (np)
3442d283bddSGavin Shan *np = p;
3452d283bddSGavin Shan if (nc)
3462d283bddSGavin Shan *nc = c;
3472d283bddSGavin Shan }
3482d283bddSGavin Shan
3492d283bddSGavin Shan /* For two consecutive NCSI commands, the packet IDs shouldn't
3502d283bddSGavin Shan * be same. Otherwise, the bogus response might be replied. So
3512d283bddSGavin Shan * the available IDs are allocated in round-robin fashion.
3522d283bddSGavin Shan */
ncsi_alloc_request(struct ncsi_dev_priv * ndp,unsigned int req_flags)353a0509cbeSGavin Shan struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
354a0509cbeSGavin Shan unsigned int req_flags)
3552d283bddSGavin Shan {
3562d283bddSGavin Shan struct ncsi_request *nr = NULL;
3572d283bddSGavin Shan int i, limit = ARRAY_SIZE(ndp->requests);
3582d283bddSGavin Shan unsigned long flags;
3592d283bddSGavin Shan
3602d283bddSGavin Shan /* Check if there is one available request until the ceiling */
3612d283bddSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
362a15af54fSGavin Shan for (i = ndp->request_id; i < limit; i++) {
3632d283bddSGavin Shan if (ndp->requests[i].used)
3642d283bddSGavin Shan continue;
3652d283bddSGavin Shan
3662d283bddSGavin Shan nr = &ndp->requests[i];
3672d283bddSGavin Shan nr->used = true;
368a0509cbeSGavin Shan nr->flags = req_flags;
369a15af54fSGavin Shan ndp->request_id = i + 1;
370a15af54fSGavin Shan goto found;
3712d283bddSGavin Shan }
3722d283bddSGavin Shan
3732d283bddSGavin Shan /* Fail back to check from the starting cursor */
374a15af54fSGavin Shan for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
3752d283bddSGavin Shan if (ndp->requests[i].used)
3762d283bddSGavin Shan continue;
3772d283bddSGavin Shan
3782d283bddSGavin Shan nr = &ndp->requests[i];
3792d283bddSGavin Shan nr->used = true;
380a0509cbeSGavin Shan nr->flags = req_flags;
381a15af54fSGavin Shan ndp->request_id = i + 1;
382a15af54fSGavin Shan goto found;
3832d283bddSGavin Shan }
3842d283bddSGavin Shan
385a15af54fSGavin Shan found:
386a15af54fSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
3872d283bddSGavin Shan return nr;
3882d283bddSGavin Shan }
3892d283bddSGavin Shan
ncsi_free_request(struct ncsi_request * nr)3902d283bddSGavin Shan void ncsi_free_request(struct ncsi_request *nr)
3912d283bddSGavin Shan {
3922d283bddSGavin Shan struct ncsi_dev_priv *ndp = nr->ndp;
3932d283bddSGavin Shan struct sk_buff *cmd, *rsp;
3942d283bddSGavin Shan unsigned long flags;
395e6f44ed6SGavin Shan bool driven;
3962d283bddSGavin Shan
3972d283bddSGavin Shan if (nr->enabled) {
3982d283bddSGavin Shan nr->enabled = false;
3992d283bddSGavin Shan del_timer_sync(&nr->timer);
4002d283bddSGavin Shan }
4012d283bddSGavin Shan
4022d283bddSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
4032d283bddSGavin Shan cmd = nr->cmd;
4042d283bddSGavin Shan rsp = nr->rsp;
4052d283bddSGavin Shan nr->cmd = NULL;
4062d283bddSGavin Shan nr->rsp = NULL;
4072d283bddSGavin Shan nr->used = false;
408a0509cbeSGavin Shan driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
4092d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
4102d283bddSGavin Shan
411e6f44ed6SGavin Shan if (driven && cmd && --ndp->pending_req_num == 0)
412e6f44ed6SGavin Shan schedule_work(&ndp->work);
413e6f44ed6SGavin Shan
4142d283bddSGavin Shan /* Release command and response */
4152d283bddSGavin Shan consume_skb(cmd);
4162d283bddSGavin Shan consume_skb(rsp);
4172d283bddSGavin Shan }
4182d283bddSGavin Shan
ncsi_find_dev(struct net_device * dev)4192d283bddSGavin Shan struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
4202d283bddSGavin Shan {
4212d283bddSGavin Shan struct ncsi_dev_priv *ndp;
4222d283bddSGavin Shan
4232d283bddSGavin Shan NCSI_FOR_EACH_DEV(ndp) {
4242d283bddSGavin Shan if (ndp->ndev.dev == dev)
4252d283bddSGavin Shan return &ndp->ndev;
4262d283bddSGavin Shan }
4272d283bddSGavin Shan
4282d283bddSGavin Shan return NULL;
4292d283bddSGavin Shan }
4302d283bddSGavin Shan
ncsi_request_timeout(struct timer_list * t)431e99e88a9SKees Cook static void ncsi_request_timeout(struct timer_list *t)
4322d283bddSGavin Shan {
433e99e88a9SKees Cook struct ncsi_request *nr = from_timer(nr, t, timer);
4342d283bddSGavin Shan struct ncsi_dev_priv *ndp = nr->ndp;
4359771b8ccSJustin.Lee1@Dell.com struct ncsi_cmd_pkt *cmd;
4369771b8ccSJustin.Lee1@Dell.com struct ncsi_package *np;
4379771b8ccSJustin.Lee1@Dell.com struct ncsi_channel *nc;
4382d283bddSGavin Shan unsigned long flags;
4392d283bddSGavin Shan
4402d283bddSGavin Shan /* If the request already had associated response,
4412d283bddSGavin Shan * let the response handler to release it.
4422d283bddSGavin Shan */
4432d283bddSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
4442d283bddSGavin Shan nr->enabled = false;
4452d283bddSGavin Shan if (nr->rsp || !nr->cmd) {
4462d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
4472d283bddSGavin Shan return;
4482d283bddSGavin Shan }
4492d283bddSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
4502d283bddSGavin Shan
4519771b8ccSJustin.Lee1@Dell.com if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
4529771b8ccSJustin.Lee1@Dell.com if (nr->cmd) {
4539771b8ccSJustin.Lee1@Dell.com /* Find the package */
4549771b8ccSJustin.Lee1@Dell.com cmd = (struct ncsi_cmd_pkt *)
4559771b8ccSJustin.Lee1@Dell.com skb_network_header(nr->cmd);
4569771b8ccSJustin.Lee1@Dell.com ncsi_find_package_and_channel(ndp,
4579771b8ccSJustin.Lee1@Dell.com cmd->cmd.common.channel,
4589771b8ccSJustin.Lee1@Dell.com &np, &nc);
4599771b8ccSJustin.Lee1@Dell.com ncsi_send_netlink_timeout(nr, np, nc);
4609771b8ccSJustin.Lee1@Dell.com }
4619771b8ccSJustin.Lee1@Dell.com }
4629771b8ccSJustin.Lee1@Dell.com
4632d283bddSGavin Shan /* Release the request */
4642d283bddSGavin Shan ncsi_free_request(nr);
4652d283bddSGavin Shan }
4662d283bddSGavin Shan
ncsi_suspend_channel(struct ncsi_dev_priv * ndp)467e6f44ed6SGavin Shan static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
468e6f44ed6SGavin Shan {
469e6f44ed6SGavin Shan struct ncsi_dev *nd = &ndp->ndev;
470cd09ab09SSamuel Mendoza-Jonas struct ncsi_package *np;
471cd09ab09SSamuel Mendoza-Jonas struct ncsi_channel *nc, *tmp;
472e6f44ed6SGavin Shan struct ncsi_cmd_arg nca;
473d8cedaabSGavin Shan unsigned long flags;
474e6f44ed6SGavin Shan int ret;
475e6f44ed6SGavin Shan
476cd09ab09SSamuel Mendoza-Jonas np = ndp->active_package;
477cd09ab09SSamuel Mendoza-Jonas nc = ndp->active_channel;
478e6f44ed6SGavin Shan nca.ndp = ndp;
479a0509cbeSGavin Shan nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
480e6f44ed6SGavin Shan switch (nd->state) {
481e6f44ed6SGavin Shan case ncsi_dev_state_suspend:
482e6f44ed6SGavin Shan nd->state = ncsi_dev_state_suspend_select;
483df561f66SGustavo A. R. Silva fallthrough;
484e6f44ed6SGavin Shan case ncsi_dev_state_suspend_select:
485e6f44ed6SGavin Shan ndp->pending_req_num = 1;
486e6f44ed6SGavin Shan
487e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_SP;
4887ba5c003SGavin Shan nca.package = np->id;
489bc7e0f50SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
490e6f44ed6SGavin Shan if (ndp->flags & NCSI_DEV_HWA)
491e6f44ed6SGavin Shan nca.bytes[0] = 0;
492e6f44ed6SGavin Shan else
493e6f44ed6SGavin Shan nca.bytes[0] = 1;
4947ba5c003SGavin Shan
495008a424aSGavin Shan /* To retrieve the last link states of channels in current
496008a424aSGavin Shan * package when current active channel needs fail over to
497008a424aSGavin Shan * another one. It means we will possibly select another
498008a424aSGavin Shan * channel as next active one. The link states of channels
499008a424aSGavin Shan * are most important factor of the selection. So we need
500008a424aSGavin Shan * accurate link states. Unfortunately, the link states on
501008a424aSGavin Shan * inactive channels can't be updated with LSC AEN in time.
502008a424aSGavin Shan */
503008a424aSGavin Shan if (ndp->flags & NCSI_DEV_RESHUFFLE)
504008a424aSGavin Shan nd->state = ncsi_dev_state_suspend_gls;
505008a424aSGavin Shan else
506e6f44ed6SGavin Shan nd->state = ncsi_dev_state_suspend_dcnt;
5077ba5c003SGavin Shan ret = ncsi_xmit_cmd(&nca);
5087ba5c003SGavin Shan if (ret)
5097ba5c003SGavin Shan goto error;
5107ba5c003SGavin Shan
5117ba5c003SGavin Shan break;
512008a424aSGavin Shan case ncsi_dev_state_suspend_gls:
513645e643eSDelphineCCChiu ndp->pending_req_num = 1;
514008a424aSGavin Shan
515008a424aSGavin Shan nca.type = NCSI_PKT_CMD_GLS;
516008a424aSGavin Shan nca.package = np->id;
517645e643eSDelphineCCChiu nca.channel = ndp->channel_probe_id;
518008a424aSGavin Shan ret = ncsi_xmit_cmd(&nca);
519008a424aSGavin Shan if (ret)
520008a424aSGavin Shan goto error;
521645e643eSDelphineCCChiu ndp->channel_probe_id++;
522645e643eSDelphineCCChiu
523645e643eSDelphineCCChiu if (ndp->channel_probe_id == ndp->channel_count) {
524645e643eSDelphineCCChiu ndp->channel_probe_id = 0;
525645e643eSDelphineCCChiu nd->state = ncsi_dev_state_suspend_dcnt;
526008a424aSGavin Shan }
527008a424aSGavin Shan
528008a424aSGavin Shan break;
5297ba5c003SGavin Shan case ncsi_dev_state_suspend_dcnt:
5307ba5c003SGavin Shan ndp->pending_req_num = 1;
5317ba5c003SGavin Shan
532e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_DCNT;
5337ba5c003SGavin Shan nca.package = np->id;
534e6f44ed6SGavin Shan nca.channel = nc->id;
5357ba5c003SGavin Shan
536e6f44ed6SGavin Shan nd->state = ncsi_dev_state_suspend_dc;
5377ba5c003SGavin Shan ret = ncsi_xmit_cmd(&nca);
5387ba5c003SGavin Shan if (ret)
5397ba5c003SGavin Shan goto error;
5407ba5c003SGavin Shan
5417ba5c003SGavin Shan break;
5427ba5c003SGavin Shan case ncsi_dev_state_suspend_dc:
5437ba5c003SGavin Shan ndp->pending_req_num = 1;
5447ba5c003SGavin Shan
545e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_DC;
5467ba5c003SGavin Shan nca.package = np->id;
547e6f44ed6SGavin Shan nca.channel = nc->id;
548e6f44ed6SGavin Shan nca.bytes[0] = 1;
549e6f44ed6SGavin Shan
5507ba5c003SGavin Shan nd->state = ncsi_dev_state_suspend_deselect;
551e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
5527ba5c003SGavin Shan if (ret)
5537ba5c003SGavin Shan goto error;
5547ba5c003SGavin Shan
555cd09ab09SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, tmp) {
556cd09ab09SSamuel Mendoza-Jonas /* If there is another channel active on this package
557cd09ab09SSamuel Mendoza-Jonas * do not deselect the package.
558cd09ab09SSamuel Mendoza-Jonas */
559cd09ab09SSamuel Mendoza-Jonas if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
560cd09ab09SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_suspend_done;
561cd09ab09SSamuel Mendoza-Jonas break;
562cd09ab09SSamuel Mendoza-Jonas }
563cd09ab09SSamuel Mendoza-Jonas }
5647ba5c003SGavin Shan break;
5657ba5c003SGavin Shan case ncsi_dev_state_suspend_deselect:
5667ba5c003SGavin Shan ndp->pending_req_num = 1;
5677ba5c003SGavin Shan
5687ba5c003SGavin Shan nca.type = NCSI_PKT_CMD_DP;
5697ba5c003SGavin Shan nca.package = np->id;
5707ba5c003SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
5717ba5c003SGavin Shan
5727ba5c003SGavin Shan nd->state = ncsi_dev_state_suspend_done;
5737ba5c003SGavin Shan ret = ncsi_xmit_cmd(&nca);
5747ba5c003SGavin Shan if (ret)
5757ba5c003SGavin Shan goto error;
576e6f44ed6SGavin Shan
577e6f44ed6SGavin Shan break;
578e6f44ed6SGavin Shan case ncsi_dev_state_suspend_done:
579d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
580d8cedaabSGavin Shan nc->state = NCSI_CHANNEL_INACTIVE;
581d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
5822878a2cfSSamuel Mendoza-Jonas if (ndp->flags & NCSI_DEV_RESET)
5832878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(nd);
5842878a2cfSSamuel Mendoza-Jonas else
585e6f44ed6SGavin Shan ncsi_process_next_channel(ndp);
586e6f44ed6SGavin Shan break;
587e6f44ed6SGavin Shan default:
588e6f44ed6SGavin Shan netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
589e6f44ed6SGavin Shan nd->state);
590e6f44ed6SGavin Shan }
5917ba5c003SGavin Shan
5927ba5c003SGavin Shan return;
5937ba5c003SGavin Shan error:
5947ba5c003SGavin Shan nd->state = ncsi_dev_state_functional;
595e6f44ed6SGavin Shan }
596e6f44ed6SGavin Shan
59721acf630SSamuel Mendoza-Jonas /* Check the VLAN filter bitmap for a set filter, and construct a
59821acf630SSamuel Mendoza-Jonas * "Set VLAN Filter - Disable" packet if found.
59921acf630SSamuel Mendoza-Jonas */
clear_one_vid(struct ncsi_dev_priv * ndp,struct ncsi_channel * nc,struct ncsi_cmd_arg * nca)60021acf630SSamuel Mendoza-Jonas static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
60121acf630SSamuel Mendoza-Jonas struct ncsi_cmd_arg *nca)
60221acf630SSamuel Mendoza-Jonas {
603062b3e1bSSamuel Mendoza-Jonas struct ncsi_channel_vlan_filter *ncf;
604062b3e1bSSamuel Mendoza-Jonas unsigned long flags;
605062b3e1bSSamuel Mendoza-Jonas void *bitmap;
60621acf630SSamuel Mendoza-Jonas int index;
60721acf630SSamuel Mendoza-Jonas u16 vid;
60821acf630SSamuel Mendoza-Jonas
609062b3e1bSSamuel Mendoza-Jonas ncf = &nc->vlan_filter;
610062b3e1bSSamuel Mendoza-Jonas bitmap = &ncf->bitmap;
611062b3e1bSSamuel Mendoza-Jonas
612062b3e1bSSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
613b5c7e7ecSYury Norov index = find_first_bit(bitmap, ncf->n_vids);
614062b3e1bSSamuel Mendoza-Jonas if (index >= ncf->n_vids) {
615062b3e1bSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
61621acf630SSamuel Mendoza-Jonas return -1;
61721acf630SSamuel Mendoza-Jonas }
618062b3e1bSSamuel Mendoza-Jonas vid = ncf->vids[index];
61921acf630SSamuel Mendoza-Jonas
620062b3e1bSSamuel Mendoza-Jonas clear_bit(index, bitmap);
621062b3e1bSSamuel Mendoza-Jonas ncf->vids[index] = 0;
622062b3e1bSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
62321acf630SSamuel Mendoza-Jonas
62421acf630SSamuel Mendoza-Jonas nca->type = NCSI_PKT_CMD_SVF;
62521acf630SSamuel Mendoza-Jonas nca->words[1] = vid;
62621acf630SSamuel Mendoza-Jonas /* HW filter index starts at 1 */
62721acf630SSamuel Mendoza-Jonas nca->bytes[6] = index + 1;
62821acf630SSamuel Mendoza-Jonas nca->bytes[7] = 0x00;
62921acf630SSamuel Mendoza-Jonas return 0;
63021acf630SSamuel Mendoza-Jonas }
63121acf630SSamuel Mendoza-Jonas
6324fb3ebbfSZheng Yongjun /* Find an outstanding VLAN tag and construct a "Set VLAN Filter - Enable"
63321acf630SSamuel Mendoza-Jonas * packet.
63421acf630SSamuel Mendoza-Jonas */
set_one_vid(struct ncsi_dev_priv * ndp,struct ncsi_channel * nc,struct ncsi_cmd_arg * nca)63521acf630SSamuel Mendoza-Jonas static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
63621acf630SSamuel Mendoza-Jonas struct ncsi_cmd_arg *nca)
63721acf630SSamuel Mendoza-Jonas {
638062b3e1bSSamuel Mendoza-Jonas struct ncsi_channel_vlan_filter *ncf;
63921acf630SSamuel Mendoza-Jonas struct vlan_vid *vlan = NULL;
640062b3e1bSSamuel Mendoza-Jonas unsigned long flags;
641062b3e1bSSamuel Mendoza-Jonas int i, index;
642062b3e1bSSamuel Mendoza-Jonas void *bitmap;
643062b3e1bSSamuel Mendoza-Jonas u16 vid;
64421acf630SSamuel Mendoza-Jonas
645062b3e1bSSamuel Mendoza-Jonas if (list_empty(&ndp->vlan_vids))
646062b3e1bSSamuel Mendoza-Jonas return -1;
647062b3e1bSSamuel Mendoza-Jonas
648062b3e1bSSamuel Mendoza-Jonas ncf = &nc->vlan_filter;
649062b3e1bSSamuel Mendoza-Jonas bitmap = &ncf->bitmap;
650062b3e1bSSamuel Mendoza-Jonas
651062b3e1bSSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
652062b3e1bSSamuel Mendoza-Jonas
653062b3e1bSSamuel Mendoza-Jonas rcu_read_lock();
65421acf630SSamuel Mendoza-Jonas list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
655062b3e1bSSamuel Mendoza-Jonas vid = vlan->vid;
656062b3e1bSSamuel Mendoza-Jonas for (i = 0; i < ncf->n_vids; i++)
657062b3e1bSSamuel Mendoza-Jonas if (ncf->vids[i] == vid) {
658062b3e1bSSamuel Mendoza-Jonas vid = 0;
65921acf630SSamuel Mendoza-Jonas break;
66021acf630SSamuel Mendoza-Jonas }
661062b3e1bSSamuel Mendoza-Jonas if (vid)
662062b3e1bSSamuel Mendoza-Jonas break;
66321acf630SSamuel Mendoza-Jonas }
664062b3e1bSSamuel Mendoza-Jonas rcu_read_unlock();
66521acf630SSamuel Mendoza-Jonas
666062b3e1bSSamuel Mendoza-Jonas if (!vid) {
667062b3e1bSSamuel Mendoza-Jonas /* No VLAN ID is not set */
668062b3e1bSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
66921acf630SSamuel Mendoza-Jonas return -1;
67021acf630SSamuel Mendoza-Jonas }
67121acf630SSamuel Mendoza-Jonas
672b5c7e7ecSYury Norov index = find_first_zero_bit(bitmap, ncf->n_vids);
673062b3e1bSSamuel Mendoza-Jonas if (index < 0 || index >= ncf->n_vids) {
6746e9c0075SSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
6756e9c0075SSamuel Mendoza-Jonas "Channel %u already has all VLAN filters set\n",
6766e9c0075SSamuel Mendoza-Jonas nc->id);
677062b3e1bSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
67821acf630SSamuel Mendoza-Jonas return -1;
67921acf630SSamuel Mendoza-Jonas }
68021acf630SSamuel Mendoza-Jonas
681062b3e1bSSamuel Mendoza-Jonas ncf->vids[index] = vid;
682062b3e1bSSamuel Mendoza-Jonas set_bit(index, bitmap);
683062b3e1bSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
684062b3e1bSSamuel Mendoza-Jonas
68521acf630SSamuel Mendoza-Jonas nca->type = NCSI_PKT_CMD_SVF;
686062b3e1bSSamuel Mendoza-Jonas nca->words[1] = vid;
68721acf630SSamuel Mendoza-Jonas /* HW filter index starts at 1 */
68821acf630SSamuel Mendoza-Jonas nca->bytes[6] = index + 1;
68921acf630SSamuel Mendoza-Jonas nca->bytes[7] = 0x01;
69021acf630SSamuel Mendoza-Jonas
69121acf630SSamuel Mendoza-Jonas return 0;
69221acf630SSamuel Mendoza-Jonas }
69321acf630SSamuel Mendoza-Jonas
ncsi_oem_keep_phy_intel(struct ncsi_cmd_arg * nca)694abd2fddcSIvan Mikhaylov static int ncsi_oem_keep_phy_intel(struct ncsi_cmd_arg *nca)
695abd2fddcSIvan Mikhaylov {
696abd2fddcSIvan Mikhaylov unsigned char data[NCSI_OEM_INTEL_CMD_KEEP_PHY_LEN];
697abd2fddcSIvan Mikhaylov int ret = 0;
698abd2fddcSIvan Mikhaylov
699abd2fddcSIvan Mikhaylov nca->payload = NCSI_OEM_INTEL_CMD_KEEP_PHY_LEN;
700abd2fddcSIvan Mikhaylov
701abd2fddcSIvan Mikhaylov memset(data, 0, NCSI_OEM_INTEL_CMD_KEEP_PHY_LEN);
702abd2fddcSIvan Mikhaylov *(unsigned int *)data = ntohl((__force __be32)NCSI_OEM_MFR_INTEL_ID);
703abd2fddcSIvan Mikhaylov
704abd2fddcSIvan Mikhaylov data[4] = NCSI_OEM_INTEL_CMD_KEEP_PHY;
705abd2fddcSIvan Mikhaylov
706abd2fddcSIvan Mikhaylov /* PHY Link up attribute */
707abd2fddcSIvan Mikhaylov data[6] = 0x1;
708abd2fddcSIvan Mikhaylov
709abd2fddcSIvan Mikhaylov nca->data = data;
710abd2fddcSIvan Mikhaylov
711abd2fddcSIvan Mikhaylov ret = ncsi_xmit_cmd(nca);
712abd2fddcSIvan Mikhaylov if (ret)
713abd2fddcSIvan Mikhaylov netdev_err(nca->ndp->ndev.dev,
714abd2fddcSIvan Mikhaylov "NCSI: Failed to transmit cmd 0x%x during configure\n",
715abd2fddcSIvan Mikhaylov nca->type);
716abd2fddcSIvan Mikhaylov return ret;
717abd2fddcSIvan Mikhaylov }
718abd2fddcSIvan Mikhaylov
719cb10c7c0SVijay Khemka /* NCSI OEM Command APIs */
ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg * nca)720cb10c7c0SVijay Khemka static int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
721cb10c7c0SVijay Khemka {
722cb10c7c0SVijay Khemka unsigned char data[NCSI_OEM_BCM_CMD_GMA_LEN];
723cb10c7c0SVijay Khemka int ret = 0;
724cb10c7c0SVijay Khemka
725cb10c7c0SVijay Khemka nca->payload = NCSI_OEM_BCM_CMD_GMA_LEN;
726cb10c7c0SVijay Khemka
727cb10c7c0SVijay Khemka memset(data, 0, NCSI_OEM_BCM_CMD_GMA_LEN);
72827fa107dSIvan Mikhaylov *(unsigned int *)data = ntohl((__force __be32)NCSI_OEM_MFR_BCM_ID);
729cb10c7c0SVijay Khemka data[5] = NCSI_OEM_BCM_CMD_GMA;
730cb10c7c0SVijay Khemka
731cb10c7c0SVijay Khemka nca->data = data;
732cb10c7c0SVijay Khemka
733cb10c7c0SVijay Khemka ret = ncsi_xmit_cmd(nca);
734cb10c7c0SVijay Khemka if (ret)
735cb10c7c0SVijay Khemka netdev_err(nca->ndp->ndev.dev,
736cb10c7c0SVijay Khemka "NCSI: Failed to transmit cmd 0x%x during configure\n",
737cb10c7c0SVijay Khemka nca->type);
738cb10c7c0SVijay Khemka return ret;
739cb10c7c0SVijay Khemka }
740cb10c7c0SVijay Khemka
ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg * nca)74116e8c4caSVijay Khemka static int ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca)
74216e8c4caSVijay Khemka {
74316e8c4caSVijay Khemka union {
74416e8c4caSVijay Khemka u8 data_u8[NCSI_OEM_MLX_CMD_GMA_LEN];
74516e8c4caSVijay Khemka u32 data_u32[NCSI_OEM_MLX_CMD_GMA_LEN / sizeof(u32)];
74616e8c4caSVijay Khemka } u;
74716e8c4caSVijay Khemka int ret = 0;
74816e8c4caSVijay Khemka
74916e8c4caSVijay Khemka nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN;
75016e8c4caSVijay Khemka
75116e8c4caSVijay Khemka memset(&u, 0, sizeof(u));
75227fa107dSIvan Mikhaylov u.data_u32[0] = ntohl((__force __be32)NCSI_OEM_MFR_MLX_ID);
75316e8c4caSVijay Khemka u.data_u8[5] = NCSI_OEM_MLX_CMD_GMA;
75416e8c4caSVijay Khemka u.data_u8[6] = NCSI_OEM_MLX_CMD_GMA_PARAM;
75516e8c4caSVijay Khemka
75616e8c4caSVijay Khemka nca->data = u.data_u8;
75716e8c4caSVijay Khemka
75816e8c4caSVijay Khemka ret = ncsi_xmit_cmd(nca);
75916e8c4caSVijay Khemka if (ret)
76016e8c4caSVijay Khemka netdev_err(nca->ndp->ndev.dev,
76116e8c4caSVijay Khemka "NCSI: Failed to transmit cmd 0x%x during configure\n",
76216e8c4caSVijay Khemka nca->type);
76316e8c4caSVijay Khemka return ret;
76416e8c4caSVijay Khemka }
76516e8c4caSVijay Khemka
ncsi_oem_smaf_mlx(struct ncsi_cmd_arg * nca)7665e0fcc16SVijay Khemka static int ncsi_oem_smaf_mlx(struct ncsi_cmd_arg *nca)
7675e0fcc16SVijay Khemka {
7685e0fcc16SVijay Khemka union {
7695e0fcc16SVijay Khemka u8 data_u8[NCSI_OEM_MLX_CMD_SMAF_LEN];
7705e0fcc16SVijay Khemka u32 data_u32[NCSI_OEM_MLX_CMD_SMAF_LEN / sizeof(u32)];
7715e0fcc16SVijay Khemka } u;
7725e0fcc16SVijay Khemka int ret = 0;
7735e0fcc16SVijay Khemka
7745e0fcc16SVijay Khemka memset(&u, 0, sizeof(u));
77527fa107dSIvan Mikhaylov u.data_u32[0] = ntohl((__force __be32)NCSI_OEM_MFR_MLX_ID);
7765e0fcc16SVijay Khemka u.data_u8[5] = NCSI_OEM_MLX_CMD_SMAF;
7775e0fcc16SVijay Khemka u.data_u8[6] = NCSI_OEM_MLX_CMD_SMAF_PARAM;
7785e0fcc16SVijay Khemka memcpy(&u.data_u8[MLX_SMAF_MAC_ADDR_OFFSET],
7795e0fcc16SVijay Khemka nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
7805e0fcc16SVijay Khemka u.data_u8[MLX_SMAF_MED_SUPPORT_OFFSET] =
7815e0fcc16SVijay Khemka (MLX_MC_RBT_AVL | MLX_MC_RBT_SUPPORT);
7825e0fcc16SVijay Khemka
7835e0fcc16SVijay Khemka nca->payload = NCSI_OEM_MLX_CMD_SMAF_LEN;
7845e0fcc16SVijay Khemka nca->data = u.data_u8;
7855e0fcc16SVijay Khemka
7865e0fcc16SVijay Khemka ret = ncsi_xmit_cmd(nca);
7875e0fcc16SVijay Khemka if (ret)
7885e0fcc16SVijay Khemka netdev_err(nca->ndp->ndev.dev,
7895e0fcc16SVijay Khemka "NCSI: Failed to transmit cmd 0x%x during probe\n",
7905e0fcc16SVijay Khemka nca->type);
7915e0fcc16SVijay Khemka return ret;
7925e0fcc16SVijay Khemka }
7935e0fcc16SVijay Khemka
ncsi_oem_gma_handler_intel(struct ncsi_cmd_arg * nca)794205b95feSIvan Mikhaylov static int ncsi_oem_gma_handler_intel(struct ncsi_cmd_arg *nca)
795205b95feSIvan Mikhaylov {
796205b95feSIvan Mikhaylov unsigned char data[NCSI_OEM_INTEL_CMD_GMA_LEN];
797205b95feSIvan Mikhaylov int ret = 0;
798205b95feSIvan Mikhaylov
799205b95feSIvan Mikhaylov nca->payload = NCSI_OEM_INTEL_CMD_GMA_LEN;
800205b95feSIvan Mikhaylov
801205b95feSIvan Mikhaylov memset(data, 0, NCSI_OEM_INTEL_CMD_GMA_LEN);
802205b95feSIvan Mikhaylov *(unsigned int *)data = ntohl((__force __be32)NCSI_OEM_MFR_INTEL_ID);
803205b95feSIvan Mikhaylov data[4] = NCSI_OEM_INTEL_CMD_GMA;
804205b95feSIvan Mikhaylov
805205b95feSIvan Mikhaylov nca->data = data;
806205b95feSIvan Mikhaylov
807205b95feSIvan Mikhaylov ret = ncsi_xmit_cmd(nca);
808205b95feSIvan Mikhaylov if (ret)
809205b95feSIvan Mikhaylov netdev_err(nca->ndp->ndev.dev,
810205b95feSIvan Mikhaylov "NCSI: Failed to transmit cmd 0x%x during configure\n",
811205b95feSIvan Mikhaylov nca->type);
812205b95feSIvan Mikhaylov
813205b95feSIvan Mikhaylov return ret;
814205b95feSIvan Mikhaylov }
815205b95feSIvan Mikhaylov
816cb10c7c0SVijay Khemka /* OEM Command handlers initialization */
817cb10c7c0SVijay Khemka static struct ncsi_oem_gma_handler {
818cb10c7c0SVijay Khemka unsigned int mfr_id;
819cb10c7c0SVijay Khemka int (*handler)(struct ncsi_cmd_arg *nca);
820cb10c7c0SVijay Khemka } ncsi_oem_gma_handlers[] = {
82116e8c4caSVijay Khemka { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm },
822205b95feSIvan Mikhaylov { NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx },
823205b95feSIvan Mikhaylov { NCSI_OEM_MFR_INTEL_ID, ncsi_oem_gma_handler_intel }
824cb10c7c0SVijay Khemka };
825cb10c7c0SVijay Khemka
ncsi_gma_handler(struct ncsi_cmd_arg * nca,unsigned int mf_id)826cb10c7c0SVijay Khemka static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
827cb10c7c0SVijay Khemka {
828cb10c7c0SVijay Khemka struct ncsi_oem_gma_handler *nch = NULL;
829cb10c7c0SVijay Khemka int i;
830cb10c7c0SVijay Khemka
831cb10c7c0SVijay Khemka /* This function should only be called once, return if flag set */
832cb10c7c0SVijay Khemka if (nca->ndp->gma_flag == 1)
833cb10c7c0SVijay Khemka return -1;
834cb10c7c0SVijay Khemka
835cb10c7c0SVijay Khemka /* Find gma handler for given manufacturer id */
836cb10c7c0SVijay Khemka for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) {
837cb10c7c0SVijay Khemka if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) {
838cb10c7c0SVijay Khemka if (ncsi_oem_gma_handlers[i].handler)
839cb10c7c0SVijay Khemka nch = &ncsi_oem_gma_handlers[i];
840cb10c7c0SVijay Khemka break;
841cb10c7c0SVijay Khemka }
842cb10c7c0SVijay Khemka }
843cb10c7c0SVijay Khemka
844cb10c7c0SVijay Khemka if (!nch) {
845cb10c7c0SVijay Khemka netdev_err(nca->ndp->ndev.dev,
846cb10c7c0SVijay Khemka "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
847cb10c7c0SVijay Khemka mf_id);
848cb10c7c0SVijay Khemka return -1;
849cb10c7c0SVijay Khemka }
850cb10c7c0SVijay Khemka
851cb10c7c0SVijay Khemka /* Get Mac address from NCSI device */
852cb10c7c0SVijay Khemka return nch->handler(nca);
853cb10c7c0SVijay Khemka }
854cb10c7c0SVijay Khemka
8558d951a75SSamuel Mendoza-Jonas /* Determine if a given channel from the channel_queue should be used for Tx */
ncsi_channel_is_tx(struct ncsi_dev_priv * ndp,struct ncsi_channel * nc)8568d951a75SSamuel Mendoza-Jonas static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
8578d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc)
8588d951a75SSamuel Mendoza-Jonas {
8598d951a75SSamuel Mendoza-Jonas struct ncsi_channel_mode *ncm;
8608d951a75SSamuel Mendoza-Jonas struct ncsi_channel *channel;
8618d951a75SSamuel Mendoza-Jonas struct ncsi_package *np;
8628d951a75SSamuel Mendoza-Jonas
8638d951a75SSamuel Mendoza-Jonas /* Check if any other channel has Tx enabled; a channel may have already
8648d951a75SSamuel Mendoza-Jonas * been configured and removed from the channel queue.
8658d951a75SSamuel Mendoza-Jonas */
8668d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) {
8678d951a75SSamuel Mendoza-Jonas if (!ndp->multi_package && np != nc->package)
8688d951a75SSamuel Mendoza-Jonas continue;
8698d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, channel) {
8708d951a75SSamuel Mendoza-Jonas ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
8718d951a75SSamuel Mendoza-Jonas if (ncm->enable)
8728d951a75SSamuel Mendoza-Jonas return false;
8738d951a75SSamuel Mendoza-Jonas }
8748d951a75SSamuel Mendoza-Jonas }
8758d951a75SSamuel Mendoza-Jonas
8768d951a75SSamuel Mendoza-Jonas /* This channel is the preferred channel and has link */
8778d951a75SSamuel Mendoza-Jonas list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
8788d951a75SSamuel Mendoza-Jonas np = channel->package;
8798d951a75SSamuel Mendoza-Jonas if (np->preferred_channel &&
8808d951a75SSamuel Mendoza-Jonas ncsi_channel_has_link(np->preferred_channel)) {
8818d951a75SSamuel Mendoza-Jonas return np->preferred_channel == nc;
8828d951a75SSamuel Mendoza-Jonas }
8838d951a75SSamuel Mendoza-Jonas }
8848d951a75SSamuel Mendoza-Jonas
8858d951a75SSamuel Mendoza-Jonas /* This channel has link */
8868d951a75SSamuel Mendoza-Jonas if (ncsi_channel_has_link(nc))
8878d951a75SSamuel Mendoza-Jonas return true;
8888d951a75SSamuel Mendoza-Jonas
8898d951a75SSamuel Mendoza-Jonas list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
8908d951a75SSamuel Mendoza-Jonas if (ncsi_channel_has_link(channel))
8918d951a75SSamuel Mendoza-Jonas return false;
8928d951a75SSamuel Mendoza-Jonas
8938d951a75SSamuel Mendoza-Jonas /* No other channel has link; default to this one */
8948d951a75SSamuel Mendoza-Jonas return true;
8958d951a75SSamuel Mendoza-Jonas }
8968d951a75SSamuel Mendoza-Jonas
8978d951a75SSamuel Mendoza-Jonas /* Change the active Tx channel in a multi-channel setup */
ncsi_update_tx_channel(struct ncsi_dev_priv * ndp,struct ncsi_package * package,struct ncsi_channel * disable,struct ncsi_channel * enable)8988d951a75SSamuel Mendoza-Jonas int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
8998d951a75SSamuel Mendoza-Jonas struct ncsi_package *package,
9008d951a75SSamuel Mendoza-Jonas struct ncsi_channel *disable,
9018d951a75SSamuel Mendoza-Jonas struct ncsi_channel *enable)
9028d951a75SSamuel Mendoza-Jonas {
9038d951a75SSamuel Mendoza-Jonas struct ncsi_cmd_arg nca;
9048d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc;
9058d951a75SSamuel Mendoza-Jonas struct ncsi_package *np;
9068d951a75SSamuel Mendoza-Jonas int ret = 0;
9078d951a75SSamuel Mendoza-Jonas
9088d951a75SSamuel Mendoza-Jonas if (!package->multi_channel && !ndp->multi_package)
9098d951a75SSamuel Mendoza-Jonas netdev_warn(ndp->ndev.dev,
9108d951a75SSamuel Mendoza-Jonas "NCSI: Trying to update Tx channel in single-channel mode\n");
9118d951a75SSamuel Mendoza-Jonas nca.ndp = ndp;
9128d951a75SSamuel Mendoza-Jonas nca.req_flags = 0;
9138d951a75SSamuel Mendoza-Jonas
9148d951a75SSamuel Mendoza-Jonas /* Find current channel with Tx enabled */
9158d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) {
9168d951a75SSamuel Mendoza-Jonas if (disable)
9178d951a75SSamuel Mendoza-Jonas break;
9188d951a75SSamuel Mendoza-Jonas if (!ndp->multi_package && np != package)
9198d951a75SSamuel Mendoza-Jonas continue;
9208d951a75SSamuel Mendoza-Jonas
9218d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc)
9228d951a75SSamuel Mendoza-Jonas if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
9238d951a75SSamuel Mendoza-Jonas disable = nc;
9248d951a75SSamuel Mendoza-Jonas break;
9258d951a75SSamuel Mendoza-Jonas }
9268d951a75SSamuel Mendoza-Jonas }
9278d951a75SSamuel Mendoza-Jonas
9288d951a75SSamuel Mendoza-Jonas /* Find a suitable channel for Tx */
9298d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) {
9308d951a75SSamuel Mendoza-Jonas if (enable)
9318d951a75SSamuel Mendoza-Jonas break;
9328d951a75SSamuel Mendoza-Jonas if (!ndp->multi_package && np != package)
9338d951a75SSamuel Mendoza-Jonas continue;
9348d951a75SSamuel Mendoza-Jonas if (!(ndp->package_whitelist & (0x1 << np->id)))
9358d951a75SSamuel Mendoza-Jonas continue;
9368d951a75SSamuel Mendoza-Jonas
9378d951a75SSamuel Mendoza-Jonas if (np->preferred_channel &&
9388d951a75SSamuel Mendoza-Jonas ncsi_channel_has_link(np->preferred_channel)) {
9398d951a75SSamuel Mendoza-Jonas enable = np->preferred_channel;
9408d951a75SSamuel Mendoza-Jonas break;
9418d951a75SSamuel Mendoza-Jonas }
9428d951a75SSamuel Mendoza-Jonas
9438d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) {
9448d951a75SSamuel Mendoza-Jonas if (!(np->channel_whitelist & 0x1 << nc->id))
9458d951a75SSamuel Mendoza-Jonas continue;
9468d951a75SSamuel Mendoza-Jonas if (nc->state != NCSI_CHANNEL_ACTIVE)
9478d951a75SSamuel Mendoza-Jonas continue;
9488d951a75SSamuel Mendoza-Jonas if (ncsi_channel_has_link(nc)) {
9498d951a75SSamuel Mendoza-Jonas enable = nc;
9508d951a75SSamuel Mendoza-Jonas break;
9518d951a75SSamuel Mendoza-Jonas }
9528d951a75SSamuel Mendoza-Jonas }
9538d951a75SSamuel Mendoza-Jonas }
9548d951a75SSamuel Mendoza-Jonas
9558d951a75SSamuel Mendoza-Jonas if (disable == enable)
9568d951a75SSamuel Mendoza-Jonas return -1;
9578d951a75SSamuel Mendoza-Jonas
9588d951a75SSamuel Mendoza-Jonas if (!enable)
9598d951a75SSamuel Mendoza-Jonas return -1;
9608d951a75SSamuel Mendoza-Jonas
9618d951a75SSamuel Mendoza-Jonas if (disable) {
9628d951a75SSamuel Mendoza-Jonas nca.channel = disable->id;
9638d951a75SSamuel Mendoza-Jonas nca.package = disable->package->id;
9648d951a75SSamuel Mendoza-Jonas nca.type = NCSI_PKT_CMD_DCNT;
9658d951a75SSamuel Mendoza-Jonas ret = ncsi_xmit_cmd(&nca);
9668d951a75SSamuel Mendoza-Jonas if (ret)
9678d951a75SSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
9688d951a75SSamuel Mendoza-Jonas "Error %d sending DCNT\n",
9698d951a75SSamuel Mendoza-Jonas ret);
9708d951a75SSamuel Mendoza-Jonas }
9718d951a75SSamuel Mendoza-Jonas
9728d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
9738d951a75SSamuel Mendoza-Jonas
9748d951a75SSamuel Mendoza-Jonas nca.channel = enable->id;
9758d951a75SSamuel Mendoza-Jonas nca.package = enable->package->id;
9768d951a75SSamuel Mendoza-Jonas nca.type = NCSI_PKT_CMD_ECNT;
9778d951a75SSamuel Mendoza-Jonas ret = ncsi_xmit_cmd(&nca);
9788d951a75SSamuel Mendoza-Jonas if (ret)
9798d951a75SSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
9808d951a75SSamuel Mendoza-Jonas "Error %d sending ECNT\n",
9818d951a75SSamuel Mendoza-Jonas ret);
9828d951a75SSamuel Mendoza-Jonas
9838d951a75SSamuel Mendoza-Jonas return ret;
9848d951a75SSamuel Mendoza-Jonas }
9858d951a75SSamuel Mendoza-Jonas
ncsi_configure_channel(struct ncsi_dev_priv * ndp)986e6f44ed6SGavin Shan static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
987e6f44ed6SGavin Shan {
988e6f44ed6SGavin Shan struct ncsi_package *np = ndp->active_package;
989e6f44ed6SGavin Shan struct ncsi_channel *nc = ndp->active_channel;
990bbc7c01eSGavin Shan struct ncsi_channel *hot_nc = NULL;
9918d951a75SSamuel Mendoza-Jonas struct ncsi_dev *nd = &ndp->ndev;
9928d951a75SSamuel Mendoza-Jonas struct net_device *dev = nd->dev;
993e6f44ed6SGavin Shan struct ncsi_cmd_arg nca;
994e6f44ed6SGavin Shan unsigned char index;
995d8cedaabSGavin Shan unsigned long flags;
996e6f44ed6SGavin Shan int ret;
997e6f44ed6SGavin Shan
998e6f44ed6SGavin Shan nca.ndp = ndp;
999a0509cbeSGavin Shan nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
1000e6f44ed6SGavin Shan switch (nd->state) {
1001e6f44ed6SGavin Shan case ncsi_dev_state_config:
1002e6f44ed6SGavin Shan case ncsi_dev_state_config_sp:
1003e6f44ed6SGavin Shan ndp->pending_req_num = 1;
1004e6f44ed6SGavin Shan
1005e6f44ed6SGavin Shan /* Select the specific package */
1006e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_SP;
1007e6f44ed6SGavin Shan if (ndp->flags & NCSI_DEV_HWA)
1008e6f44ed6SGavin Shan nca.bytes[0] = 0;
1009e6f44ed6SGavin Shan else
1010e6f44ed6SGavin Shan nca.bytes[0] = 1;
1011e6f44ed6SGavin Shan nca.package = np->id;
1012bc7e0f50SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
1013e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
10149ef8690bSSamuel Mendoza-Jonas if (ret) {
10159ef8690bSSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
10169ef8690bSSamuel Mendoza-Jonas "NCSI: Failed to transmit CMD_SP\n");
1017e6f44ed6SGavin Shan goto error;
10189ef8690bSSamuel Mendoza-Jonas }
1019e6f44ed6SGavin Shan
1020e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_cis;
1021e6f44ed6SGavin Shan break;
1022e6f44ed6SGavin Shan case ncsi_dev_state_config_cis:
1023e6f44ed6SGavin Shan ndp->pending_req_num = 1;
1024e6f44ed6SGavin Shan
1025e6f44ed6SGavin Shan /* Clear initial state */
1026e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_CIS;
1027e6f44ed6SGavin Shan nca.package = np->id;
1028e6f44ed6SGavin Shan nca.channel = nc->id;
1029e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
10309ef8690bSSamuel Mendoza-Jonas if (ret) {
10319ef8690bSSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
10329ef8690bSSamuel Mendoza-Jonas "NCSI: Failed to transmit CMD_CIS\n");
1033e6f44ed6SGavin Shan goto error;
10349ef8690bSSamuel Mendoza-Jonas }
1035e6f44ed6SGavin Shan
1036d7dd9d1fSPeter Delevoryas nd->state = IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
1037d7dd9d1fSPeter Delevoryas ? ncsi_dev_state_config_oem_gma
1038d7dd9d1fSPeter Delevoryas : ncsi_dev_state_config_clear_vids;
1039cb10c7c0SVijay Khemka break;
1040cb10c7c0SVijay Khemka case ncsi_dev_state_config_oem_gma:
1041ba32f06eSPaul Fertser nd->state = ncsi_dev_state_config_apply_mac;
1042cb10c7c0SVijay Khemka
1043cb10c7c0SVijay Khemka nca.package = np->id;
1044cb10c7c0SVijay Khemka nca.channel = nc->id;
1045cb10c7c0SVijay Khemka ndp->pending_req_num = 1;
104667515088SPeter Delevoryas if (nc->version.major >= 1 && nc->version.minor >= 2) {
104767515088SPeter Delevoryas nca.type = NCSI_PKT_CMD_GMCMA;
104867515088SPeter Delevoryas ret = ncsi_xmit_cmd(&nca);
104967515088SPeter Delevoryas } else {
105067515088SPeter Delevoryas nca.type = NCSI_PKT_CMD_OEM;
1051cb10c7c0SVijay Khemka ret = ncsi_gma_handler(&nca, nc->version.mf_id);
105267515088SPeter Delevoryas }
1053ba32f06eSPaul Fertser if (ret < 0) {
1054ba32f06eSPaul Fertser nd->state = ncsi_dev_state_config_clear_vids;
1055cb10c7c0SVijay Khemka schedule_work(&ndp->work);
1056ba32f06eSPaul Fertser }
1057cb10c7c0SVijay Khemka
1058e6f44ed6SGavin Shan break;
1059ba32f06eSPaul Fertser case ncsi_dev_state_config_apply_mac:
1060ba32f06eSPaul Fertser rtnl_lock();
1061ba32f06eSPaul Fertser ret = dev_set_mac_address(dev, &ndp->pending_mac, NULL);
1062ba32f06eSPaul Fertser rtnl_unlock();
1063ba32f06eSPaul Fertser if (ret < 0)
1064ba32f06eSPaul Fertser netdev_warn(dev, "NCSI: 'Writing MAC address to device failed\n");
1065ba32f06eSPaul Fertser
1066ba32f06eSPaul Fertser nd->state = ncsi_dev_state_config_clear_vids;
1067ba32f06eSPaul Fertser
1068ba32f06eSPaul Fertser fallthrough;
106921acf630SSamuel Mendoza-Jonas case ncsi_dev_state_config_clear_vids:
107021acf630SSamuel Mendoza-Jonas case ncsi_dev_state_config_svf:
107121acf630SSamuel Mendoza-Jonas case ncsi_dev_state_config_ev:
1072e6f44ed6SGavin Shan case ncsi_dev_state_config_sma:
1073e6f44ed6SGavin Shan case ncsi_dev_state_config_ebf:
1074cf0eba33SVijay Khemka case ncsi_dev_state_config_dgmf:
1075e6f44ed6SGavin Shan case ncsi_dev_state_config_ecnt:
1076e6f44ed6SGavin Shan case ncsi_dev_state_config_ec:
1077e6f44ed6SGavin Shan case ncsi_dev_state_config_ae:
1078e6f44ed6SGavin Shan case ncsi_dev_state_config_gls:
1079e6f44ed6SGavin Shan ndp->pending_req_num = 1;
1080e6f44ed6SGavin Shan
1081e6f44ed6SGavin Shan nca.package = np->id;
1082e6f44ed6SGavin Shan nca.channel = nc->id;
1083e6f44ed6SGavin Shan
108421acf630SSamuel Mendoza-Jonas /* Clear any active filters on the channel before setting */
108521acf630SSamuel Mendoza-Jonas if (nd->state == ncsi_dev_state_config_clear_vids) {
108621acf630SSamuel Mendoza-Jonas ret = clear_one_vid(ndp, nc, &nca);
108721acf630SSamuel Mendoza-Jonas if (ret) {
108821acf630SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_svf;
108921acf630SSamuel Mendoza-Jonas schedule_work(&ndp->work);
109021acf630SSamuel Mendoza-Jonas break;
109121acf630SSamuel Mendoza-Jonas }
109221acf630SSamuel Mendoza-Jonas /* Repeat */
109321acf630SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_clear_vids;
109421acf630SSamuel Mendoza-Jonas /* Add known VLAN tags to the filter */
109521acf630SSamuel Mendoza-Jonas } else if (nd->state == ncsi_dev_state_config_svf) {
109621acf630SSamuel Mendoza-Jonas ret = set_one_vid(ndp, nc, &nca);
109721acf630SSamuel Mendoza-Jonas if (ret) {
109821acf630SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_ev;
109921acf630SSamuel Mendoza-Jonas schedule_work(&ndp->work);
110021acf630SSamuel Mendoza-Jonas break;
110121acf630SSamuel Mendoza-Jonas }
110221acf630SSamuel Mendoza-Jonas /* Repeat */
110321acf630SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_svf;
110421acf630SSamuel Mendoza-Jonas /* Enable/Disable the VLAN filter */
110521acf630SSamuel Mendoza-Jonas } else if (nd->state == ncsi_dev_state_config_ev) {
110621acf630SSamuel Mendoza-Jonas if (list_empty(&ndp->vlan_vids)) {
110721acf630SSamuel Mendoza-Jonas nca.type = NCSI_PKT_CMD_DV;
110821acf630SSamuel Mendoza-Jonas } else {
110921acf630SSamuel Mendoza-Jonas nca.type = NCSI_PKT_CMD_EV;
111021acf630SSamuel Mendoza-Jonas nca.bytes[3] = NCSI_CAP_VLAN_NO;
111121acf630SSamuel Mendoza-Jonas }
111221acf630SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_sma;
111321acf630SSamuel Mendoza-Jonas } else if (nd->state == ncsi_dev_state_config_sma) {
1114e6f44ed6SGavin Shan /* Use first entry in unicast filter table. Note that
1115e6f44ed6SGavin Shan * the MAC filter table starts from entry 1 instead of
1116e6f44ed6SGavin Shan * 0.
1117e6f44ed6SGavin Shan */
1118e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_SMA;
1119e6f44ed6SGavin Shan for (index = 0; index < 6; index++)
1120e6f44ed6SGavin Shan nca.bytes[index] = dev->dev_addr[index];
1121e6f44ed6SGavin Shan nca.bytes[6] = 0x1;
1122e6f44ed6SGavin Shan nca.bytes[7] = 0x1;
1123e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_ebf;
1124e6f44ed6SGavin Shan } else if (nd->state == ncsi_dev_state_config_ebf) {
1125e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_EBF;
1126e6f44ed6SGavin Shan nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
1127cf0eba33SVijay Khemka /* if multicast global filtering is supported then
1128cf0eba33SVijay Khemka * disable it so that all multicast packet will be
1129cf0eba33SVijay Khemka * forwarded to management controller
1130cf0eba33SVijay Khemka */
1131cf0eba33SVijay Khemka if (nc->caps[NCSI_CAP_GENERIC].cap &
1132cf0eba33SVijay Khemka NCSI_CAP_GENERIC_MC)
1133cf0eba33SVijay Khemka nd->state = ncsi_dev_state_config_dgmf;
1134cf0eba33SVijay Khemka else if (ncsi_channel_is_tx(ndp, nc))
1135cf0eba33SVijay Khemka nd->state = ncsi_dev_state_config_ecnt;
1136cf0eba33SVijay Khemka else
1137cf0eba33SVijay Khemka nd->state = ncsi_dev_state_config_ec;
1138cf0eba33SVijay Khemka } else if (nd->state == ncsi_dev_state_config_dgmf) {
1139cf0eba33SVijay Khemka nca.type = NCSI_PKT_CMD_DGMF;
11408d951a75SSamuel Mendoza-Jonas if (ncsi_channel_is_tx(ndp, nc))
1141e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_ecnt;
11428d951a75SSamuel Mendoza-Jonas else
11438d951a75SSamuel Mendoza-Jonas nd->state = ncsi_dev_state_config_ec;
1144e6f44ed6SGavin Shan } else if (nd->state == ncsi_dev_state_config_ecnt) {
11458d951a75SSamuel Mendoza-Jonas if (np->preferred_channel &&
11468d951a75SSamuel Mendoza-Jonas nc != np->preferred_channel)
11478d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev,
11488d951a75SSamuel Mendoza-Jonas "NCSI: Tx failed over to channel %u\n",
11498d951a75SSamuel Mendoza-Jonas nc->id);
1150e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_ECNT;
1151e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_ec;
1152e6f44ed6SGavin Shan } else if (nd->state == ncsi_dev_state_config_ec) {
1153e6f44ed6SGavin Shan /* Enable AEN if it's supported */
1154e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_EC;
1155e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_ae;
1156e6f44ed6SGavin Shan if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
1157e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_gls;
1158e6f44ed6SGavin Shan } else if (nd->state == ncsi_dev_state_config_ae) {
1159e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_AE;
1160e6f44ed6SGavin Shan nca.bytes[0] = 0;
1161e6f44ed6SGavin Shan nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
1162e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_gls;
1163e6f44ed6SGavin Shan } else if (nd->state == ncsi_dev_state_config_gls) {
1164e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_GLS;
1165e6f44ed6SGavin Shan nd->state = ncsi_dev_state_config_done;
1166e6f44ed6SGavin Shan }
1167e6f44ed6SGavin Shan
1168e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
11699ef8690bSSamuel Mendoza-Jonas if (ret) {
11709ef8690bSSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
11719ef8690bSSamuel Mendoza-Jonas "NCSI: Failed to transmit CMD %x\n",
11729ef8690bSSamuel Mendoza-Jonas nca.type);
1173e6f44ed6SGavin Shan goto error;
11749ef8690bSSamuel Mendoza-Jonas }
1175e6f44ed6SGavin Shan break;
1176e6f44ed6SGavin Shan case ncsi_dev_state_config_done:
11776e42a3f5SJoel Stanley netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
11786e42a3f5SJoel Stanley nc->id);
1179d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
11802878a2cfSSamuel Mendoza-Jonas nc->state = NCSI_CHANNEL_ACTIVE;
11812878a2cfSSamuel Mendoza-Jonas
11822878a2cfSSamuel Mendoza-Jonas if (ndp->flags & NCSI_DEV_RESET) {
11832878a2cfSSamuel Mendoza-Jonas /* A reset event happened during config, start it now */
11842878a2cfSSamuel Mendoza-Jonas nc->reconfigure_needed = false;
11852878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
11862878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(nd);
11872878a2cfSSamuel Mendoza-Jonas break;
11882878a2cfSSamuel Mendoza-Jonas }
11892878a2cfSSamuel Mendoza-Jonas
119021acf630SSamuel Mendoza-Jonas if (nc->reconfigure_needed) {
119121acf630SSamuel Mendoza-Jonas /* This channel's configuration has been updated
119221acf630SSamuel Mendoza-Jonas * part-way during the config state - start the
119321acf630SSamuel Mendoza-Jonas * channel configuration over
119421acf630SSamuel Mendoza-Jonas */
119521acf630SSamuel Mendoza-Jonas nc->reconfigure_needed = false;
119621acf630SSamuel Mendoza-Jonas nc->state = NCSI_CHANNEL_INACTIVE;
119721acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
119821acf630SSamuel Mendoza-Jonas
119921acf630SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
120021acf630SSamuel Mendoza-Jonas list_add_tail_rcu(&nc->link, &ndp->channel_queue);
120121acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
120221acf630SSamuel Mendoza-Jonas
12036e42a3f5SJoel Stanley netdev_dbg(dev, "Dirty NCSI channel state reset\n");
120421acf630SSamuel Mendoza-Jonas ncsi_process_next_channel(ndp);
120521acf630SSamuel Mendoza-Jonas break;
120621acf630SSamuel Mendoza-Jonas }
120721acf630SSamuel Mendoza-Jonas
1208bbc7c01eSGavin Shan if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
1209bbc7c01eSGavin Shan hot_nc = nc;
1210bbc7c01eSGavin Shan } else {
1211bbc7c01eSGavin Shan hot_nc = NULL;
121287975a01SJoel Stanley netdev_dbg(ndp->ndev.dev,
12139ef8690bSSamuel Mendoza-Jonas "NCSI: channel %u link down after config\n",
12149ef8690bSSamuel Mendoza-Jonas nc->id);
1215bbc7c01eSGavin Shan }
1216d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
1217e6f44ed6SGavin Shan
1218bbc7c01eSGavin Shan /* Update the hot channel */
1219bbc7c01eSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
1220bbc7c01eSGavin Shan ndp->hot_channel = hot_nc;
1221bbc7c01eSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
1222bbc7c01eSGavin Shan
1223e6f44ed6SGavin Shan ncsi_start_channel_monitor(nc);
1224e6f44ed6SGavin Shan ncsi_process_next_channel(ndp);
1225e6f44ed6SGavin Shan break;
1226e6f44ed6SGavin Shan default:
12279ef8690bSSamuel Mendoza-Jonas netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
1228e6f44ed6SGavin Shan nd->state);
1229e6f44ed6SGavin Shan }
1230e6f44ed6SGavin Shan
1231e6f44ed6SGavin Shan return;
1232e6f44ed6SGavin Shan
1233e6f44ed6SGavin Shan error:
1234e6f44ed6SGavin Shan ncsi_report_link(ndp, true);
1235e6f44ed6SGavin Shan }
1236e6f44ed6SGavin Shan
ncsi_choose_active_channel(struct ncsi_dev_priv * ndp)1237e6f44ed6SGavin Shan static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
1238e6f44ed6SGavin Shan {
12398d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc, *found, *hot_nc;
1240e6f44ed6SGavin Shan struct ncsi_channel_mode *ncm;
12418d951a75SSamuel Mendoza-Jonas unsigned long flags, cflags;
12428d951a75SSamuel Mendoza-Jonas struct ncsi_package *np;
12438d951a75SSamuel Mendoza-Jonas bool with_link;
1244e6f44ed6SGavin Shan
1245bbc7c01eSGavin Shan spin_lock_irqsave(&ndp->lock, flags);
1246bbc7c01eSGavin Shan hot_nc = ndp->hot_channel;
1247bbc7c01eSGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
1248bbc7c01eSGavin Shan
12498d951a75SSamuel Mendoza-Jonas /* By default the search is done once an inactive channel with up
12508d951a75SSamuel Mendoza-Jonas * link is found, unless a preferred channel is set.
12518d951a75SSamuel Mendoza-Jonas * If multi_package or multi_channel are configured all channels in the
12528d951a75SSamuel Mendoza-Jonas * whitelist are added to the channel queue.
1253e6f44ed6SGavin Shan */
1254e6f44ed6SGavin Shan found = NULL;
12558d951a75SSamuel Mendoza-Jonas with_link = false;
1256e6f44ed6SGavin Shan NCSI_FOR_EACH_PACKAGE(ndp, np) {
12578d951a75SSamuel Mendoza-Jonas if (!(ndp->package_whitelist & (0x1 << np->id)))
1258955dc68cSSamuel Mendoza-Jonas continue;
1259e6f44ed6SGavin Shan NCSI_FOR_EACH_CHANNEL(np, nc) {
12608d951a75SSamuel Mendoza-Jonas if (!(np->channel_whitelist & (0x1 << nc->id)))
12618d951a75SSamuel Mendoza-Jonas continue;
12628d951a75SSamuel Mendoza-Jonas
12638d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, cflags);
1264d8cedaabSGavin Shan
1265e6f44ed6SGavin Shan if (!list_empty(&nc->link) ||
1266d8cedaabSGavin Shan nc->state != NCSI_CHANNEL_INACTIVE) {
12678d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, cflags);
1268e6f44ed6SGavin Shan continue;
1269d8cedaabSGavin Shan }
1270e6f44ed6SGavin Shan
1271e6f44ed6SGavin Shan if (!found)
1272e6f44ed6SGavin Shan found = nc;
1273e6f44ed6SGavin Shan
1274bbc7c01eSGavin Shan if (nc == hot_nc)
1275bbc7c01eSGavin Shan found = nc;
1276bbc7c01eSGavin Shan
1277e6f44ed6SGavin Shan ncm = &nc->modes[NCSI_MODE_LINK];
1278e6f44ed6SGavin Shan if (ncm->data[2] & 0x1) {
1279e6f44ed6SGavin Shan found = nc;
12808d951a75SSamuel Mendoza-Jonas with_link = true;
1281e6f44ed6SGavin Shan }
1282d8cedaabSGavin Shan
12838d951a75SSamuel Mendoza-Jonas /* If multi_channel is enabled configure all valid
12848d951a75SSamuel Mendoza-Jonas * channels whether or not they currently have link
12858d951a75SSamuel Mendoza-Jonas * so they will have AENs enabled.
12868d951a75SSamuel Mendoza-Jonas */
12878d951a75SSamuel Mendoza-Jonas if (with_link || np->multi_channel) {
12888d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
12898d951a75SSamuel Mendoza-Jonas list_add_tail_rcu(&nc->link,
12908d951a75SSamuel Mendoza-Jonas &ndp->channel_queue);
12918d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
1292e6f44ed6SGavin Shan
12936e42a3f5SJoel Stanley netdev_dbg(ndp->ndev.dev,
12949ef8690bSSamuel Mendoza-Jonas "NCSI: Channel %u added to queue (link %s)\n",
12958d951a75SSamuel Mendoza-Jonas nc->id,
12968d951a75SSamuel Mendoza-Jonas ncm->data[2] & 0x1 ? "up" : "down");
12978d951a75SSamuel Mendoza-Jonas }
12989ef8690bSSamuel Mendoza-Jonas
12998d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, cflags);
13008d951a75SSamuel Mendoza-Jonas
13018d951a75SSamuel Mendoza-Jonas if (with_link && !np->multi_channel)
13028d951a75SSamuel Mendoza-Jonas break;
13038d951a75SSamuel Mendoza-Jonas }
13048d951a75SSamuel Mendoza-Jonas if (with_link && !ndp->multi_package)
13058d951a75SSamuel Mendoza-Jonas break;
13068d951a75SSamuel Mendoza-Jonas }
13078d951a75SSamuel Mendoza-Jonas
13088d951a75SSamuel Mendoza-Jonas if (list_empty(&ndp->channel_queue) && found) {
13098d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev,
13108d951a75SSamuel Mendoza-Jonas "NCSI: No channel with link found, configuring channel %u\n",
13118d951a75SSamuel Mendoza-Jonas found->id);
1312e6f44ed6SGavin Shan spin_lock_irqsave(&ndp->lock, flags);
1313e6f44ed6SGavin Shan list_add_tail_rcu(&found->link, &ndp->channel_queue);
1314e6f44ed6SGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
13158d951a75SSamuel Mendoza-Jonas } else if (!found) {
13168d951a75SSamuel Mendoza-Jonas netdev_warn(ndp->ndev.dev,
13178d951a75SSamuel Mendoza-Jonas "NCSI: No channel found to configure!\n");
13188d951a75SSamuel Mendoza-Jonas ncsi_report_link(ndp, true);
13198d951a75SSamuel Mendoza-Jonas return -ENODEV;
13208d951a75SSamuel Mendoza-Jonas }
1321e6f44ed6SGavin Shan
1322e6f44ed6SGavin Shan return ncsi_process_next_channel(ndp);
1323e6f44ed6SGavin Shan }
1324e6f44ed6SGavin Shan
ncsi_check_hwa(struct ncsi_dev_priv * ndp)1325e6f44ed6SGavin Shan static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
1326e6f44ed6SGavin Shan {
1327e6f44ed6SGavin Shan struct ncsi_package *np;
1328e6f44ed6SGavin Shan struct ncsi_channel *nc;
1329e6f44ed6SGavin Shan unsigned int cap;
1330100ef01fSGavin Shan bool has_channel = false;
1331e6f44ed6SGavin Shan
1332e6f44ed6SGavin Shan /* The hardware arbitration is disabled if any one channel
1333e6f44ed6SGavin Shan * doesn't support explicitly.
1334e6f44ed6SGavin Shan */
1335e6f44ed6SGavin Shan NCSI_FOR_EACH_PACKAGE(ndp, np) {
1336e6f44ed6SGavin Shan NCSI_FOR_EACH_CHANNEL(np, nc) {
1337100ef01fSGavin Shan has_channel = true;
1338100ef01fSGavin Shan
1339e6f44ed6SGavin Shan cap = nc->caps[NCSI_CAP_GENERIC].cap;
1340e6f44ed6SGavin Shan if (!(cap & NCSI_CAP_GENERIC_HWA) ||
1341e6f44ed6SGavin Shan (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
1342e6f44ed6SGavin Shan NCSI_CAP_GENERIC_HWA_SUPPORT) {
1343e6f44ed6SGavin Shan ndp->flags &= ~NCSI_DEV_HWA;
1344e6f44ed6SGavin Shan return false;
1345e6f44ed6SGavin Shan }
1346e6f44ed6SGavin Shan }
1347e6f44ed6SGavin Shan }
1348e6f44ed6SGavin Shan
1349100ef01fSGavin Shan if (has_channel) {
1350e6f44ed6SGavin Shan ndp->flags |= NCSI_DEV_HWA;
1351e6f44ed6SGavin Shan return true;
1352e6f44ed6SGavin Shan }
1353e6f44ed6SGavin Shan
1354100ef01fSGavin Shan ndp->flags &= ~NCSI_DEV_HWA;
1355100ef01fSGavin Shan return false;
1356100ef01fSGavin Shan }
1357100ef01fSGavin Shan
ncsi_probe_channel(struct ncsi_dev_priv * ndp)1358e6f44ed6SGavin Shan static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
1359e6f44ed6SGavin Shan {
1360e6f44ed6SGavin Shan struct ncsi_dev *nd = &ndp->ndev;
1361e6f44ed6SGavin Shan struct ncsi_package *np;
1362e6f44ed6SGavin Shan struct ncsi_cmd_arg nca;
1363e6f44ed6SGavin Shan unsigned char index;
1364e6f44ed6SGavin Shan int ret;
1365e6f44ed6SGavin Shan
1366e6f44ed6SGavin Shan nca.ndp = ndp;
1367a0509cbeSGavin Shan nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
1368e6f44ed6SGavin Shan switch (nd->state) {
1369e6f44ed6SGavin Shan case ncsi_dev_state_probe:
1370e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe_deselect;
1371df561f66SGustavo A. R. Silva fallthrough;
1372e6f44ed6SGavin Shan case ncsi_dev_state_probe_deselect:
1373e6f44ed6SGavin Shan ndp->pending_req_num = 8;
1374e6f44ed6SGavin Shan
1375e6f44ed6SGavin Shan /* Deselect all possible packages */
1376e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_DP;
1377bc7e0f50SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
1378e6f44ed6SGavin Shan for (index = 0; index < 8; index++) {
1379e6f44ed6SGavin Shan nca.package = index;
1380e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
1381e6f44ed6SGavin Shan if (ret)
1382e6f44ed6SGavin Shan goto error;
1383e6f44ed6SGavin Shan }
1384e6f44ed6SGavin Shan
1385e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe_package;
1386e6f44ed6SGavin Shan break;
1387e6f44ed6SGavin Shan case ncsi_dev_state_probe_package:
1388*ab9f2ec0SPaul Fertser if (ndp->package_probe_id >= 8) {
1389*ab9f2ec0SPaul Fertser /* Last package probed, finishing */
1390*ab9f2ec0SPaul Fertser ndp->flags |= NCSI_DEV_PROBED;
1391*ab9f2ec0SPaul Fertser break;
1392*ab9f2ec0SPaul Fertser }
1393*ab9f2ec0SPaul Fertser
13948e13f70bSSamuel Mendoza-Jonas ndp->pending_req_num = 1;
1395e6f44ed6SGavin Shan
1396e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_SP;
1397e6f44ed6SGavin Shan nca.bytes[0] = 1;
13988e13f70bSSamuel Mendoza-Jonas nca.package = ndp->package_probe_id;
1399bc7e0f50SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
1400e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
1401e6f44ed6SGavin Shan if (ret)
1402e6f44ed6SGavin Shan goto error;
1403e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe_channel;
1404e6f44ed6SGavin Shan break;
1405e6f44ed6SGavin Shan case ncsi_dev_state_probe_channel:
14068e13f70bSSamuel Mendoza-Jonas ndp->active_package = ncsi_find_package(ndp,
14078e13f70bSSamuel Mendoza-Jonas ndp->package_probe_id);
1408e6f44ed6SGavin Shan if (!ndp->active_package) {
14098e13f70bSSamuel Mendoza-Jonas /* No response */
14108e13f70bSSamuel Mendoza-Jonas nd->state = ncsi_dev_state_probe_dp;
14118e13f70bSSamuel Mendoza-Jonas schedule_work(&ndp->work);
14128e13f70bSSamuel Mendoza-Jonas break;
1413e6f44ed6SGavin Shan }
1414e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe_cis;
14155e0fcc16SVijay Khemka if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) &&
14165e0fcc16SVijay Khemka ndp->mlx_multi_host)
14175e0fcc16SVijay Khemka nd->state = ncsi_dev_state_probe_mlx_gma;
14185e0fcc16SVijay Khemka
14198e13f70bSSamuel Mendoza-Jonas schedule_work(&ndp->work);
1420e6f44ed6SGavin Shan break;
14215e0fcc16SVijay Khemka case ncsi_dev_state_probe_mlx_gma:
14225e0fcc16SVijay Khemka ndp->pending_req_num = 1;
14235e0fcc16SVijay Khemka
14245e0fcc16SVijay Khemka nca.type = NCSI_PKT_CMD_OEM;
14255e0fcc16SVijay Khemka nca.package = ndp->active_package->id;
14265e0fcc16SVijay Khemka nca.channel = 0;
14275e0fcc16SVijay Khemka ret = ncsi_oem_gma_handler_mlx(&nca);
14285e0fcc16SVijay Khemka if (ret)
14295e0fcc16SVijay Khemka goto error;
14305e0fcc16SVijay Khemka
14315e0fcc16SVijay Khemka nd->state = ncsi_dev_state_probe_mlx_smaf;
14325e0fcc16SVijay Khemka break;
14335e0fcc16SVijay Khemka case ncsi_dev_state_probe_mlx_smaf:
14345e0fcc16SVijay Khemka ndp->pending_req_num = 1;
14355e0fcc16SVijay Khemka
14365e0fcc16SVijay Khemka nca.type = NCSI_PKT_CMD_OEM;
14375e0fcc16SVijay Khemka nca.package = ndp->active_package->id;
14385e0fcc16SVijay Khemka nca.channel = 0;
14395e0fcc16SVijay Khemka ret = ncsi_oem_smaf_mlx(&nca);
14405e0fcc16SVijay Khemka if (ret)
14415e0fcc16SVijay Khemka goto error;
14425e0fcc16SVijay Khemka
14435e0fcc16SVijay Khemka nd->state = ncsi_dev_state_probe_cis;
14445e0fcc16SVijay Khemka break;
1445abd2fddcSIvan Mikhaylov case ncsi_dev_state_probe_keep_phy:
1446abd2fddcSIvan Mikhaylov ndp->pending_req_num = 1;
1447abd2fddcSIvan Mikhaylov
1448abd2fddcSIvan Mikhaylov nca.type = NCSI_PKT_CMD_OEM;
1449abd2fddcSIvan Mikhaylov nca.package = ndp->active_package->id;
1450abd2fddcSIvan Mikhaylov nca.channel = 0;
1451abd2fddcSIvan Mikhaylov ret = ncsi_oem_keep_phy_intel(&nca);
1452abd2fddcSIvan Mikhaylov if (ret)
1453abd2fddcSIvan Mikhaylov goto error;
1454abd2fddcSIvan Mikhaylov
1455abd2fddcSIvan Mikhaylov nd->state = ncsi_dev_state_probe_gvi;
1456abd2fddcSIvan Mikhaylov break;
1457645e643eSDelphineCCChiu case ncsi_dev_state_probe_cis:
1458e6f44ed6SGavin Shan case ncsi_dev_state_probe_gvi:
1459e6f44ed6SGavin Shan case ncsi_dev_state_probe_gc:
1460e6f44ed6SGavin Shan case ncsi_dev_state_probe_gls:
1461e6f44ed6SGavin Shan np = ndp->active_package;
1462645e643eSDelphineCCChiu ndp->pending_req_num = 1;
1463e6f44ed6SGavin Shan
1464645e643eSDelphineCCChiu /* Clear initial state Retrieve version, capability or link status */
1465645e643eSDelphineCCChiu if (nd->state == ncsi_dev_state_probe_cis)
1466645e643eSDelphineCCChiu nca.type = NCSI_PKT_CMD_CIS;
1467645e643eSDelphineCCChiu else if (nd->state == ncsi_dev_state_probe_gvi)
1468e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_GVI;
1469e6f44ed6SGavin Shan else if (nd->state == ncsi_dev_state_probe_gc)
1470e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_GC;
1471e6f44ed6SGavin Shan else
1472e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_GLS;
1473e6f44ed6SGavin Shan
1474e6f44ed6SGavin Shan nca.package = np->id;
1475645e643eSDelphineCCChiu nca.channel = ndp->channel_probe_id;
1476645e643eSDelphineCCChiu
1477e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
1478e6f44ed6SGavin Shan if (ret)
1479e6f44ed6SGavin Shan goto error;
1480645e643eSDelphineCCChiu
1481645e643eSDelphineCCChiu if (nd->state == ncsi_dev_state_probe_cis) {
1482645e643eSDelphineCCChiu nd->state = ncsi_dev_state_probe_gvi;
1483645e643eSDelphineCCChiu if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_KEEP_PHY) && ndp->channel_probe_id == 0)
1484645e643eSDelphineCCChiu nd->state = ncsi_dev_state_probe_keep_phy;
1485645e643eSDelphineCCChiu } else if (nd->state == ncsi_dev_state_probe_gvi) {
1486645e643eSDelphineCCChiu nd->state = ncsi_dev_state_probe_gc;
1487645e643eSDelphineCCChiu } else if (nd->state == ncsi_dev_state_probe_gc) {
1488645e643eSDelphineCCChiu nd->state = ncsi_dev_state_probe_gls;
1489645e643eSDelphineCCChiu } else {
1490645e643eSDelphineCCChiu nd->state = ncsi_dev_state_probe_cis;
1491645e643eSDelphineCCChiu ndp->channel_probe_id++;
1492e6f44ed6SGavin Shan }
1493e6f44ed6SGavin Shan
1494645e643eSDelphineCCChiu if (ndp->channel_probe_id == ndp->channel_count) {
1495645e643eSDelphineCCChiu ndp->channel_probe_id = 0;
1496e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe_dp;
1497645e643eSDelphineCCChiu }
1498e6f44ed6SGavin Shan break;
1499e6f44ed6SGavin Shan case ncsi_dev_state_probe_dp:
1500e6f44ed6SGavin Shan ndp->pending_req_num = 1;
1501e6f44ed6SGavin Shan
15028e13f70bSSamuel Mendoza-Jonas /* Deselect the current package */
1503e6f44ed6SGavin Shan nca.type = NCSI_PKT_CMD_DP;
15048e13f70bSSamuel Mendoza-Jonas nca.package = ndp->package_probe_id;
1505bc7e0f50SGavin Shan nca.channel = NCSI_RESERVED_CHANNEL;
1506e6f44ed6SGavin Shan ret = ncsi_xmit_cmd(&nca);
1507e6f44ed6SGavin Shan if (ret)
1508e6f44ed6SGavin Shan goto error;
1509e6f44ed6SGavin Shan
1510*ab9f2ec0SPaul Fertser /* Probe next package after receiving response */
15118e13f70bSSamuel Mendoza-Jonas ndp->package_probe_id++;
15128e13f70bSSamuel Mendoza-Jonas nd->state = ncsi_dev_state_probe_package;
15138e13f70bSSamuel Mendoza-Jonas ndp->active_package = NULL;
1514e6f44ed6SGavin Shan break;
1515e6f44ed6SGavin Shan default:
1516e6f44ed6SGavin Shan netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
1517e6f44ed6SGavin Shan nd->state);
1518e6f44ed6SGavin Shan }
1519e6f44ed6SGavin Shan
15208e13f70bSSamuel Mendoza-Jonas if (ndp->flags & NCSI_DEV_PROBED) {
15218e13f70bSSamuel Mendoza-Jonas /* Check if all packages have HWA support */
15228e13f70bSSamuel Mendoza-Jonas ncsi_check_hwa(ndp);
15238e13f70bSSamuel Mendoza-Jonas ncsi_choose_active_channel(ndp);
15248e13f70bSSamuel Mendoza-Jonas }
15258e13f70bSSamuel Mendoza-Jonas
1526e6f44ed6SGavin Shan return;
1527e6f44ed6SGavin Shan error:
15289ef8690bSSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev,
15299ef8690bSSamuel Mendoza-Jonas "NCSI: Failed to transmit cmd 0x%x during probe\n",
15309ef8690bSSamuel Mendoza-Jonas nca.type);
1531e6f44ed6SGavin Shan ncsi_report_link(ndp, true);
1532e6f44ed6SGavin Shan }
1533e6f44ed6SGavin Shan
ncsi_dev_work(struct work_struct * work)1534e6f44ed6SGavin Shan static void ncsi_dev_work(struct work_struct *work)
1535e6f44ed6SGavin Shan {
1536e6f44ed6SGavin Shan struct ncsi_dev_priv *ndp = container_of(work,
1537e6f44ed6SGavin Shan struct ncsi_dev_priv, work);
1538e6f44ed6SGavin Shan struct ncsi_dev *nd = &ndp->ndev;
1539e6f44ed6SGavin Shan
1540e6f44ed6SGavin Shan switch (nd->state & ncsi_dev_state_major) {
1541e6f44ed6SGavin Shan case ncsi_dev_state_probe:
1542e6f44ed6SGavin Shan ncsi_probe_channel(ndp);
1543e6f44ed6SGavin Shan break;
1544e6f44ed6SGavin Shan case ncsi_dev_state_suspend:
1545e6f44ed6SGavin Shan ncsi_suspend_channel(ndp);
1546e6f44ed6SGavin Shan break;
1547e6f44ed6SGavin Shan case ncsi_dev_state_config:
1548e6f44ed6SGavin Shan ncsi_configure_channel(ndp);
1549e6f44ed6SGavin Shan break;
1550e6f44ed6SGavin Shan default:
1551e6f44ed6SGavin Shan netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
1552e6f44ed6SGavin Shan nd->state);
1553e6f44ed6SGavin Shan }
1554e6f44ed6SGavin Shan }
1555e6f44ed6SGavin Shan
ncsi_process_next_channel(struct ncsi_dev_priv * ndp)1556e6f44ed6SGavin Shan int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
1557e6f44ed6SGavin Shan {
1558e6f44ed6SGavin Shan struct ncsi_channel *nc;
1559e6f44ed6SGavin Shan int old_state;
1560e6f44ed6SGavin Shan unsigned long flags;
1561e6f44ed6SGavin Shan
1562e6f44ed6SGavin Shan spin_lock_irqsave(&ndp->lock, flags);
1563e6f44ed6SGavin Shan nc = list_first_or_null_rcu(&ndp->channel_queue,
1564e6f44ed6SGavin Shan struct ncsi_channel, link);
1565a1b43eddSArnd Bergmann if (!nc) {
1566a1b43eddSArnd Bergmann spin_unlock_irqrestore(&ndp->lock, flags);
1567a1b43eddSArnd Bergmann goto out;
1568a1b43eddSArnd Bergmann }
1569a1b43eddSArnd Bergmann
1570e6f44ed6SGavin Shan list_del_init(&nc->link);
1571e6f44ed6SGavin Shan spin_unlock_irqrestore(&ndp->lock, flags);
1572e6f44ed6SGavin Shan
1573d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
1574d8cedaabSGavin Shan old_state = nc->state;
1575d8cedaabSGavin Shan nc->state = NCSI_CHANNEL_INVISIBLE;
1576d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
1577d8cedaabSGavin Shan
1578e6f44ed6SGavin Shan ndp->active_channel = nc;
1579a1b43eddSArnd Bergmann ndp->active_package = nc->package;
1580e6f44ed6SGavin Shan
1581e6f44ed6SGavin Shan switch (old_state) {
1582e6f44ed6SGavin Shan case NCSI_CHANNEL_INACTIVE:
1583e6f44ed6SGavin Shan ndp->ndev.state = ncsi_dev_state_config;
158487975a01SJoel Stanley netdev_dbg(ndp->ndev.dev, "NCSI: configuring channel %u\n",
15859ef8690bSSamuel Mendoza-Jonas nc->id);
1586e6f44ed6SGavin Shan ncsi_configure_channel(ndp);
1587e6f44ed6SGavin Shan break;
1588e6f44ed6SGavin Shan case NCSI_CHANNEL_ACTIVE:
1589e6f44ed6SGavin Shan ndp->ndev.state = ncsi_dev_state_suspend;
159087975a01SJoel Stanley netdev_dbg(ndp->ndev.dev, "NCSI: suspending channel %u\n",
15919ef8690bSSamuel Mendoza-Jonas nc->id);
1592e6f44ed6SGavin Shan ncsi_suspend_channel(ndp);
1593e6f44ed6SGavin Shan break;
1594e6f44ed6SGavin Shan default:
1595e6f44ed6SGavin Shan netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
1596d8cedaabSGavin Shan old_state, nc->package->id, nc->id);
1597e6f44ed6SGavin Shan ncsi_report_link(ndp, false);
1598e6f44ed6SGavin Shan return -EINVAL;
1599e6f44ed6SGavin Shan }
1600e6f44ed6SGavin Shan
1601e6f44ed6SGavin Shan return 0;
1602a1b43eddSArnd Bergmann
1603a1b43eddSArnd Bergmann out:
1604a1b43eddSArnd Bergmann ndp->active_channel = NULL;
1605a1b43eddSArnd Bergmann ndp->active_package = NULL;
1606a1b43eddSArnd Bergmann if (ndp->flags & NCSI_DEV_RESHUFFLE) {
1607a1b43eddSArnd Bergmann ndp->flags &= ~NCSI_DEV_RESHUFFLE;
1608a1b43eddSArnd Bergmann return ncsi_choose_active_channel(ndp);
1609a1b43eddSArnd Bergmann }
1610a1b43eddSArnd Bergmann
1611a1b43eddSArnd Bergmann ncsi_report_link(ndp, false);
1612a1b43eddSArnd Bergmann return -ENODEV;
1613e6f44ed6SGavin Shan }
1614e6f44ed6SGavin Shan
ncsi_kick_channels(struct ncsi_dev_priv * ndp)161521acf630SSamuel Mendoza-Jonas static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
161621acf630SSamuel Mendoza-Jonas {
161721acf630SSamuel Mendoza-Jonas struct ncsi_dev *nd = &ndp->ndev;
161821acf630SSamuel Mendoza-Jonas struct ncsi_channel *nc;
161921acf630SSamuel Mendoza-Jonas struct ncsi_package *np;
162021acf630SSamuel Mendoza-Jonas unsigned long flags;
162121acf630SSamuel Mendoza-Jonas unsigned int n = 0;
162221acf630SSamuel Mendoza-Jonas
162321acf630SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) {
162421acf630SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) {
162521acf630SSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
162621acf630SSamuel Mendoza-Jonas
162721acf630SSamuel Mendoza-Jonas /* Channels may be busy, mark dirty instead of
162821acf630SSamuel Mendoza-Jonas * kicking if;
162921acf630SSamuel Mendoza-Jonas * a) not ACTIVE (configured)
163021acf630SSamuel Mendoza-Jonas * b) in the channel_queue (to be configured)
163121acf630SSamuel Mendoza-Jonas * c) it's ndev is in the config state
163221acf630SSamuel Mendoza-Jonas */
163321acf630SSamuel Mendoza-Jonas if (nc->state != NCSI_CHANNEL_ACTIVE) {
163421acf630SSamuel Mendoza-Jonas if ((ndp->ndev.state & 0xff00) ==
163521acf630SSamuel Mendoza-Jonas ncsi_dev_state_config ||
163621acf630SSamuel Mendoza-Jonas !list_empty(&nc->link)) {
16376e42a3f5SJoel Stanley netdev_dbg(nd->dev,
16389ef8690bSSamuel Mendoza-Jonas "NCSI: channel %p marked dirty\n",
163921acf630SSamuel Mendoza-Jonas nc);
164021acf630SSamuel Mendoza-Jonas nc->reconfigure_needed = true;
164121acf630SSamuel Mendoza-Jonas }
164221acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
164321acf630SSamuel Mendoza-Jonas continue;
164421acf630SSamuel Mendoza-Jonas }
164521acf630SSamuel Mendoza-Jonas
164621acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
164721acf630SSamuel Mendoza-Jonas
164821acf630SSamuel Mendoza-Jonas ncsi_stop_channel_monitor(nc);
164921acf630SSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
165021acf630SSamuel Mendoza-Jonas nc->state = NCSI_CHANNEL_INACTIVE;
165121acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
165221acf630SSamuel Mendoza-Jonas
165321acf630SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
165421acf630SSamuel Mendoza-Jonas list_add_tail_rcu(&nc->link, &ndp->channel_queue);
165521acf630SSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
165621acf630SSamuel Mendoza-Jonas
16576e42a3f5SJoel Stanley netdev_dbg(nd->dev, "NCSI: kicked channel %p\n", nc);
165821acf630SSamuel Mendoza-Jonas n++;
165921acf630SSamuel Mendoza-Jonas }
166021acf630SSamuel Mendoza-Jonas }
166121acf630SSamuel Mendoza-Jonas
166221acf630SSamuel Mendoza-Jonas return n;
166321acf630SSamuel Mendoza-Jonas }
166421acf630SSamuel Mendoza-Jonas
ncsi_vlan_rx_add_vid(struct net_device * dev,__be16 proto,u16 vid)166521acf630SSamuel Mendoza-Jonas int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
166621acf630SSamuel Mendoza-Jonas {
166721acf630SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp;
166821acf630SSamuel Mendoza-Jonas unsigned int n_vids = 0;
166921acf630SSamuel Mendoza-Jonas struct vlan_vid *vlan;
167021acf630SSamuel Mendoza-Jonas struct ncsi_dev *nd;
167121acf630SSamuel Mendoza-Jonas bool found = false;
167221acf630SSamuel Mendoza-Jonas
167321acf630SSamuel Mendoza-Jonas if (vid == 0)
167421acf630SSamuel Mendoza-Jonas return 0;
167521acf630SSamuel Mendoza-Jonas
167621acf630SSamuel Mendoza-Jonas nd = ncsi_find_dev(dev);
167721acf630SSamuel Mendoza-Jonas if (!nd) {
16789ef8690bSSamuel Mendoza-Jonas netdev_warn(dev, "NCSI: No net_device?\n");
167921acf630SSamuel Mendoza-Jonas return 0;
168021acf630SSamuel Mendoza-Jonas }
168121acf630SSamuel Mendoza-Jonas
168221acf630SSamuel Mendoza-Jonas ndp = TO_NCSI_DEV_PRIV(nd);
168321acf630SSamuel Mendoza-Jonas
168421acf630SSamuel Mendoza-Jonas /* Add the VLAN id to our internal list */
168521acf630SSamuel Mendoza-Jonas list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
168621acf630SSamuel Mendoza-Jonas n_vids++;
168721acf630SSamuel Mendoza-Jonas if (vlan->vid == vid) {
16886e42a3f5SJoel Stanley netdev_dbg(dev, "NCSI: vid %u already registered\n",
16896e42a3f5SJoel Stanley vid);
169021acf630SSamuel Mendoza-Jonas return 0;
169121acf630SSamuel Mendoza-Jonas }
169221acf630SSamuel Mendoza-Jonas }
16936e9c0075SSamuel Mendoza-Jonas if (n_vids >= NCSI_MAX_VLAN_VIDS) {
16946e9c0075SSamuel Mendoza-Jonas netdev_warn(dev,
16956e9c0075SSamuel Mendoza-Jonas "tried to add vlan id %u but NCSI max already registered (%u)\n",
16966e9c0075SSamuel Mendoza-Jonas vid, NCSI_MAX_VLAN_VIDS);
16976e9c0075SSamuel Mendoza-Jonas return -ENOSPC;
169821acf630SSamuel Mendoza-Jonas }
169921acf630SSamuel Mendoza-Jonas
170021acf630SSamuel Mendoza-Jonas vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
170121acf630SSamuel Mendoza-Jonas if (!vlan)
170221acf630SSamuel Mendoza-Jonas return -ENOMEM;
170321acf630SSamuel Mendoza-Jonas
170421acf630SSamuel Mendoza-Jonas vlan->proto = proto;
170521acf630SSamuel Mendoza-Jonas vlan->vid = vid;
170621acf630SSamuel Mendoza-Jonas list_add_rcu(&vlan->list, &ndp->vlan_vids);
170721acf630SSamuel Mendoza-Jonas
17086e42a3f5SJoel Stanley netdev_dbg(dev, "NCSI: Added new vid %u\n", vid);
170921acf630SSamuel Mendoza-Jonas
171021acf630SSamuel Mendoza-Jonas found = ncsi_kick_channels(ndp) != 0;
171121acf630SSamuel Mendoza-Jonas
171221acf630SSamuel Mendoza-Jonas return found ? ncsi_process_next_channel(ndp) : 0;
171321acf630SSamuel Mendoza-Jonas }
1714fd0c88b7SArnd Bergmann EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
171521acf630SSamuel Mendoza-Jonas
ncsi_vlan_rx_kill_vid(struct net_device * dev,__be16 proto,u16 vid)171621acf630SSamuel Mendoza-Jonas int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
171721acf630SSamuel Mendoza-Jonas {
171821acf630SSamuel Mendoza-Jonas struct vlan_vid *vlan, *tmp;
171921acf630SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp;
172021acf630SSamuel Mendoza-Jonas struct ncsi_dev *nd;
172121acf630SSamuel Mendoza-Jonas bool found = false;
172221acf630SSamuel Mendoza-Jonas
172321acf630SSamuel Mendoza-Jonas if (vid == 0)
172421acf630SSamuel Mendoza-Jonas return 0;
172521acf630SSamuel Mendoza-Jonas
172621acf630SSamuel Mendoza-Jonas nd = ncsi_find_dev(dev);
172721acf630SSamuel Mendoza-Jonas if (!nd) {
17289ef8690bSSamuel Mendoza-Jonas netdev_warn(dev, "NCSI: no net_device?\n");
172921acf630SSamuel Mendoza-Jonas return 0;
173021acf630SSamuel Mendoza-Jonas }
173121acf630SSamuel Mendoza-Jonas
173221acf630SSamuel Mendoza-Jonas ndp = TO_NCSI_DEV_PRIV(nd);
173321acf630SSamuel Mendoza-Jonas
173421acf630SSamuel Mendoza-Jonas /* Remove the VLAN id from our internal list */
173521acf630SSamuel Mendoza-Jonas list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
173621acf630SSamuel Mendoza-Jonas if (vlan->vid == vid) {
17376e42a3f5SJoel Stanley netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
173821acf630SSamuel Mendoza-Jonas list_del_rcu(&vlan->list);
173921acf630SSamuel Mendoza-Jonas found = true;
174021acf630SSamuel Mendoza-Jonas kfree(vlan);
174121acf630SSamuel Mendoza-Jonas }
174221acf630SSamuel Mendoza-Jonas
174321acf630SSamuel Mendoza-Jonas if (!found) {
17449ef8690bSSamuel Mendoza-Jonas netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
174521acf630SSamuel Mendoza-Jonas return -EINVAL;
174621acf630SSamuel Mendoza-Jonas }
174721acf630SSamuel Mendoza-Jonas
174821acf630SSamuel Mendoza-Jonas found = ncsi_kick_channels(ndp) != 0;
174921acf630SSamuel Mendoza-Jonas
175021acf630SSamuel Mendoza-Jonas return found ? ncsi_process_next_channel(ndp) : 0;
175121acf630SSamuel Mendoza-Jonas }
1752fd0c88b7SArnd Bergmann EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
175321acf630SSamuel Mendoza-Jonas
ncsi_register_dev(struct net_device * dev,void (* handler)(struct ncsi_dev * ndev))17542d283bddSGavin Shan struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
17552d283bddSGavin Shan void (*handler)(struct ncsi_dev *ndev))
17562d283bddSGavin Shan {
17572d283bddSGavin Shan struct ncsi_dev_priv *ndp;
17582d283bddSGavin Shan struct ncsi_dev *nd;
17595e0fcc16SVijay Khemka struct platform_device *pdev;
17605e0fcc16SVijay Khemka struct device_node *np;
17612d283bddSGavin Shan unsigned long flags;
17622d283bddSGavin Shan int i;
17632d283bddSGavin Shan
17642d283bddSGavin Shan /* Check if the device has been registered or not */
17652d283bddSGavin Shan nd = ncsi_find_dev(dev);
17662d283bddSGavin Shan if (nd)
17672d283bddSGavin Shan return nd;
17682d283bddSGavin Shan
17692d283bddSGavin Shan /* Create NCSI device */
17702d283bddSGavin Shan ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
17712d283bddSGavin Shan if (!ndp)
17722d283bddSGavin Shan return NULL;
17732d283bddSGavin Shan
17742d283bddSGavin Shan nd = &ndp->ndev;
17752d283bddSGavin Shan nd->state = ncsi_dev_state_registered;
17762d283bddSGavin Shan nd->dev = dev;
17772d283bddSGavin Shan nd->handler = handler;
1778e6f44ed6SGavin Shan ndp->pending_req_num = 0;
1779e6f44ed6SGavin Shan INIT_LIST_HEAD(&ndp->channel_queue);
178021acf630SSamuel Mendoza-Jonas INIT_LIST_HEAD(&ndp->vlan_vids);
1781e6f44ed6SGavin Shan INIT_WORK(&ndp->work, ncsi_dev_work);
17828d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = UINT_MAX;
17832d283bddSGavin Shan
17842d283bddSGavin Shan /* Initialize private NCSI device */
17852d283bddSGavin Shan spin_lock_init(&ndp->lock);
17862d283bddSGavin Shan INIT_LIST_HEAD(&ndp->packages);
1787a15af54fSGavin Shan ndp->request_id = NCSI_REQ_START_IDX;
17882d283bddSGavin Shan for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
17892d283bddSGavin Shan ndp->requests[i].id = i;
17902d283bddSGavin Shan ndp->requests[i].ndp = ndp;
1791e99e88a9SKees Cook timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0);
17922d283bddSGavin Shan }
1793645e643eSDelphineCCChiu ndp->channel_count = NCSI_RESERVED_CHANNEL;
17942d283bddSGavin Shan
17952d283bddSGavin Shan spin_lock_irqsave(&ncsi_dev_lock, flags);
17962d283bddSGavin Shan list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
17972d283bddSGavin Shan spin_unlock_irqrestore(&ncsi_dev_lock, flags);
17982d283bddSGavin Shan
1799e6f44ed6SGavin Shan /* Register NCSI packet Rx handler */
1800e6f44ed6SGavin Shan ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
1801e6f44ed6SGavin Shan ndp->ptype.func = ncsi_rcv_rsp;
1802e6f44ed6SGavin Shan ndp->ptype.dev = dev;
1803e6f44ed6SGavin Shan dev_add_pack(&ndp->ptype);
1804e6f44ed6SGavin Shan
18055e0fcc16SVijay Khemka pdev = to_platform_device(dev->dev.parent);
18065e0fcc16SVijay Khemka if (pdev) {
18075e0fcc16SVijay Khemka np = pdev->dev.of_node;
18081a87e641SRob Herring if (np && (of_property_read_bool(np, "mellanox,multi-host") ||
18091a87e641SRob Herring of_property_read_bool(np, "mlx,multi-host")))
18105e0fcc16SVijay Khemka ndp->mlx_multi_host = true;
18115e0fcc16SVijay Khemka }
18125e0fcc16SVijay Khemka
18132d283bddSGavin Shan return nd;
18142d283bddSGavin Shan }
18152d283bddSGavin Shan EXPORT_SYMBOL_GPL(ncsi_register_dev);
18162d283bddSGavin Shan
ncsi_start_dev(struct ncsi_dev * nd)1817e6f44ed6SGavin Shan int ncsi_start_dev(struct ncsi_dev *nd)
1818e6f44ed6SGavin Shan {
1819e6f44ed6SGavin Shan struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1820e6f44ed6SGavin Shan
1821e6f44ed6SGavin Shan if (nd->state != ncsi_dev_state_registered &&
1822e6f44ed6SGavin Shan nd->state != ncsi_dev_state_functional)
1823e6f44ed6SGavin Shan return -ENOTTY;
1824e6f44ed6SGavin Shan
1825e6f44ed6SGavin Shan if (!(ndp->flags & NCSI_DEV_PROBED)) {
18268e13f70bSSamuel Mendoza-Jonas ndp->package_probe_id = 0;
1827645e643eSDelphineCCChiu ndp->channel_probe_id = 0;
1828e6f44ed6SGavin Shan nd->state = ncsi_dev_state_probe;
1829e6f44ed6SGavin Shan schedule_work(&ndp->work);
1830e6f44ed6SGavin Shan return 0;
1831e6f44ed6SGavin Shan }
1832e6f44ed6SGavin Shan
18332878a2cfSSamuel Mendoza-Jonas return ncsi_reset_dev(nd);
1834c0cd1ba4SGavin Shan }
1835c0cd1ba4SGavin Shan EXPORT_SYMBOL_GPL(ncsi_start_dev);
1836c0cd1ba4SGavin Shan
ncsi_stop_dev(struct ncsi_dev * nd)1837c0cd1ba4SGavin Shan void ncsi_stop_dev(struct ncsi_dev *nd)
1838c0cd1ba4SGavin Shan {
1839c0cd1ba4SGavin Shan struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1840c0cd1ba4SGavin Shan struct ncsi_package *np;
1841c0cd1ba4SGavin Shan struct ncsi_channel *nc;
1842c0cd1ba4SGavin Shan bool chained;
1843c0cd1ba4SGavin Shan int old_state;
1844c0cd1ba4SGavin Shan unsigned long flags;
1845c0cd1ba4SGavin Shan
18462878a2cfSSamuel Mendoza-Jonas /* Stop the channel monitor on any active channels. Don't reset the
18472878a2cfSSamuel Mendoza-Jonas * channel state so we know which were active when ncsi_start_dev()
18482878a2cfSSamuel Mendoza-Jonas * is next called.
18492878a2cfSSamuel Mendoza-Jonas */
1850e6f44ed6SGavin Shan NCSI_FOR_EACH_PACKAGE(ndp, np) {
1851e6f44ed6SGavin Shan NCSI_FOR_EACH_CHANNEL(np, nc) {
1852c0cd1ba4SGavin Shan ncsi_stop_channel_monitor(nc);
1853c0cd1ba4SGavin Shan
1854d8cedaabSGavin Shan spin_lock_irqsave(&nc->lock, flags);
1855d8cedaabSGavin Shan chained = !list_empty(&nc->link);
1856d8cedaabSGavin Shan old_state = nc->state;
1857d8cedaabSGavin Shan spin_unlock_irqrestore(&nc->lock, flags);
1858d8cedaabSGavin Shan
1859d8cedaabSGavin Shan WARN_ON_ONCE(chained ||
1860e6f44ed6SGavin Shan old_state == NCSI_CHANNEL_INVISIBLE);
1861e6f44ed6SGavin Shan }
1862e6f44ed6SGavin Shan }
1863e6f44ed6SGavin Shan
18646e42a3f5SJoel Stanley netdev_dbg(ndp->ndev.dev, "NCSI: Stopping device\n");
1865c0cd1ba4SGavin Shan ncsi_report_link(ndp, true);
1866e6f44ed6SGavin Shan }
1867c0cd1ba4SGavin Shan EXPORT_SYMBOL_GPL(ncsi_stop_dev);
1868e6f44ed6SGavin Shan
ncsi_reset_dev(struct ncsi_dev * nd)18692878a2cfSSamuel Mendoza-Jonas int ncsi_reset_dev(struct ncsi_dev *nd)
18702878a2cfSSamuel Mendoza-Jonas {
18712878a2cfSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
18722878a2cfSSamuel Mendoza-Jonas struct ncsi_channel *nc, *active, *tmp;
18732878a2cfSSamuel Mendoza-Jonas struct ncsi_package *np;
18742878a2cfSSamuel Mendoza-Jonas unsigned long flags;
18752878a2cfSSamuel Mendoza-Jonas
18762878a2cfSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
18772878a2cfSSamuel Mendoza-Jonas
18782878a2cfSSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) {
18792878a2cfSSamuel Mendoza-Jonas /* Haven't been called yet, check states */
18802878a2cfSSamuel Mendoza-Jonas switch (nd->state & ncsi_dev_state_major) {
18812878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_registered:
18822878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_probe:
18832878a2cfSSamuel Mendoza-Jonas /* Not even probed yet - do nothing */
18842878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
18852878a2cfSSamuel Mendoza-Jonas return 0;
18862878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_suspend:
18872878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_config:
18882878a2cfSSamuel Mendoza-Jonas /* Wait for the channel to finish its suspend/config
18892878a2cfSSamuel Mendoza-Jonas * operation; once it finishes it will check for
18902878a2cfSSamuel Mendoza-Jonas * NCSI_DEV_RESET and reset the state.
18912878a2cfSSamuel Mendoza-Jonas */
18922878a2cfSSamuel Mendoza-Jonas ndp->flags |= NCSI_DEV_RESET;
18932878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
18942878a2cfSSamuel Mendoza-Jonas return 0;
18952878a2cfSSamuel Mendoza-Jonas }
18962878a2cfSSamuel Mendoza-Jonas } else {
18972878a2cfSSamuel Mendoza-Jonas switch (nd->state) {
18982878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_suspend_done:
18992878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_config_done:
19002878a2cfSSamuel Mendoza-Jonas case ncsi_dev_state_functional:
19012878a2cfSSamuel Mendoza-Jonas /* Ok */
19022878a2cfSSamuel Mendoza-Jonas break;
19032878a2cfSSamuel Mendoza-Jonas default:
19042878a2cfSSamuel Mendoza-Jonas /* Current reset operation happening */
19052878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
19062878a2cfSSamuel Mendoza-Jonas return 0;
19072878a2cfSSamuel Mendoza-Jonas }
19082878a2cfSSamuel Mendoza-Jonas }
19092878a2cfSSamuel Mendoza-Jonas
19102878a2cfSSamuel Mendoza-Jonas if (!list_empty(&ndp->channel_queue)) {
19112878a2cfSSamuel Mendoza-Jonas /* Clear any channel queue we may have interrupted */
19122878a2cfSSamuel Mendoza-Jonas list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
19132878a2cfSSamuel Mendoza-Jonas list_del_init(&nc->link);
19142878a2cfSSamuel Mendoza-Jonas }
19152878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
19162878a2cfSSamuel Mendoza-Jonas
19172878a2cfSSamuel Mendoza-Jonas active = NULL;
19182878a2cfSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) {
19192878a2cfSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) {
19202878a2cfSSamuel Mendoza-Jonas spin_lock_irqsave(&nc->lock, flags);
19212878a2cfSSamuel Mendoza-Jonas
19222878a2cfSSamuel Mendoza-Jonas if (nc->state == NCSI_CHANNEL_ACTIVE) {
19232878a2cfSSamuel Mendoza-Jonas active = nc;
19242878a2cfSSamuel Mendoza-Jonas nc->state = NCSI_CHANNEL_INVISIBLE;
19252878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
19262878a2cfSSamuel Mendoza-Jonas ncsi_stop_channel_monitor(nc);
19272878a2cfSSamuel Mendoza-Jonas break;
19282878a2cfSSamuel Mendoza-Jonas }
19292878a2cfSSamuel Mendoza-Jonas
19302878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&nc->lock, flags);
19312878a2cfSSamuel Mendoza-Jonas }
19322878a2cfSSamuel Mendoza-Jonas if (active)
19332878a2cfSSamuel Mendoza-Jonas break;
19342878a2cfSSamuel Mendoza-Jonas }
19352878a2cfSSamuel Mendoza-Jonas
19362878a2cfSSamuel Mendoza-Jonas if (!active) {
19372878a2cfSSamuel Mendoza-Jonas /* Done */
19382878a2cfSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
19392878a2cfSSamuel Mendoza-Jonas ndp->flags &= ~NCSI_DEV_RESET;
19402878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
19412878a2cfSSamuel Mendoza-Jonas return ncsi_choose_active_channel(ndp);
19422878a2cfSSamuel Mendoza-Jonas }
19432878a2cfSSamuel Mendoza-Jonas
19442878a2cfSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags);
19452878a2cfSSamuel Mendoza-Jonas ndp->flags |= NCSI_DEV_RESET;
19462878a2cfSSamuel Mendoza-Jonas ndp->active_channel = active;
19472878a2cfSSamuel Mendoza-Jonas ndp->active_package = active->package;
19482878a2cfSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags);
19492878a2cfSSamuel Mendoza-Jonas
19502878a2cfSSamuel Mendoza-Jonas nd->state = ncsi_dev_state_suspend;
19512878a2cfSSamuel Mendoza-Jonas schedule_work(&ndp->work);
19522878a2cfSSamuel Mendoza-Jonas return 0;
19532878a2cfSSamuel Mendoza-Jonas }
19542878a2cfSSamuel Mendoza-Jonas
ncsi_unregister_dev(struct ncsi_dev * nd)19552d283bddSGavin Shan void ncsi_unregister_dev(struct ncsi_dev *nd)
19562d283bddSGavin Shan {
19572d283bddSGavin Shan struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
19582d283bddSGavin Shan struct ncsi_package *np, *tmp;
19592d283bddSGavin Shan unsigned long flags;
19602d283bddSGavin Shan
1961e6f44ed6SGavin Shan dev_remove_pack(&ndp->ptype);
1962e6f44ed6SGavin Shan
19632d283bddSGavin Shan list_for_each_entry_safe(np, tmp, &ndp->packages, node)
19642d283bddSGavin Shan ncsi_remove_package(np);
19652d283bddSGavin Shan
19662d283bddSGavin Shan spin_lock_irqsave(&ncsi_dev_lock, flags);
19672d283bddSGavin Shan list_del_rcu(&ndp->node);
19682d283bddSGavin Shan spin_unlock_irqrestore(&ncsi_dev_lock, flags);
19692d283bddSGavin Shan
19702d283bddSGavin Shan kfree(ndp);
19712d283bddSGavin Shan }
19722d283bddSGavin Shan EXPORT_SYMBOL_GPL(ncsi_unregister_dev);
1973