18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright Gavin Shan, IBM Corporation 2016.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
108c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <net/ncsi.h>
158c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
168c2ecf20Sopenharmony_ci#include <net/sock.h>
178c2ecf20Sopenharmony_ci#include <net/addrconf.h>
188c2ecf20Sopenharmony_ci#include <net/ipv6.h>
198c2ecf20Sopenharmony_ci#include <net/genetlink.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "internal.h"
228c2ecf20Sopenharmony_ci#include "ncsi-pkt.h"
238c2ecf20Sopenharmony_ci#include "ncsi-netlink.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciLIST_HEAD(ncsi_dev_list);
268c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(ncsi_dev_lock);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cibool ncsi_channel_has_link(struct ncsi_channel *channel)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cibool ncsi_channel_is_last(struct ncsi_dev_priv *ndp,
348c2ecf20Sopenharmony_ci			  struct ncsi_channel *channel)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct ncsi_package *np;
378c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np)
408c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
418c2ecf20Sopenharmony_ci			if (nc == channel)
428c2ecf20Sopenharmony_ci				continue;
438c2ecf20Sopenharmony_ci			if (nc->state == NCSI_CHANNEL_ACTIVE &&
448c2ecf20Sopenharmony_ci			    ncsi_channel_has_link(nc))
458c2ecf20Sopenharmony_ci				return false;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return true;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
548c2ecf20Sopenharmony_ci	struct ncsi_package *np;
558c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
568c2ecf20Sopenharmony_ci	unsigned long flags;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	nd->state = ncsi_dev_state_functional;
598c2ecf20Sopenharmony_ci	if (force_down) {
608c2ecf20Sopenharmony_ci		nd->link_up = 0;
618c2ecf20Sopenharmony_ci		goto report;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	nd->link_up = 0;
658c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
668c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
678c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, flags);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci			if (!list_empty(&nc->link) ||
708c2ecf20Sopenharmony_ci			    nc->state != NCSI_CHANNEL_ACTIVE) {
718c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&nc->lock, flags);
728c2ecf20Sopenharmony_ci				continue;
738c2ecf20Sopenharmony_ci			}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci			if (ncsi_channel_has_link(nc)) {
768c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&nc->lock, flags);
778c2ecf20Sopenharmony_ci				nd->link_up = 1;
788c2ecf20Sopenharmony_ci				goto report;
798c2ecf20Sopenharmony_ci			}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cireport:
868c2ecf20Sopenharmony_ci	nd->handler(nd);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void ncsi_channel_monitor(struct timer_list *t)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct ncsi_channel *nc = from_timer(nc, t, monitor.timer);
928c2ecf20Sopenharmony_ci	struct ncsi_package *np = nc->package;
938c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = np->ndp;
948c2ecf20Sopenharmony_ci	struct ncsi_channel_mode *ncm;
958c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
968c2ecf20Sopenharmony_ci	bool enabled, chained;
978c2ecf20Sopenharmony_ci	unsigned int monitor_state;
988c2ecf20Sopenharmony_ci	unsigned long flags;
998c2ecf20Sopenharmony_ci	int state, ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
1028c2ecf20Sopenharmony_ci	state = nc->state;
1038c2ecf20Sopenharmony_ci	chained = !list_empty(&nc->link);
1048c2ecf20Sopenharmony_ci	enabled = nc->monitor.enabled;
1058c2ecf20Sopenharmony_ci	monitor_state = nc->monitor.state;
1068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (!enabled)
1098c2ecf20Sopenharmony_ci		return;		/* expected race disabling timer */
1108c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(chained))
1118c2ecf20Sopenharmony_ci		goto bad_state;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (state != NCSI_CHANNEL_INACTIVE &&
1148c2ecf20Sopenharmony_ci	    state != NCSI_CHANNEL_ACTIVE) {
1158c2ecf20Sopenharmony_cibad_state:
1168c2ecf20Sopenharmony_ci		netdev_warn(ndp->ndev.dev,
1178c2ecf20Sopenharmony_ci			    "Bad NCSI monitor state channel %d 0x%x %s queue\n",
1188c2ecf20Sopenharmony_ci			    nc->id, state, chained ? "on" : "off");
1198c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nc->lock, flags);
1208c2ecf20Sopenharmony_ci		nc->monitor.enabled = false;
1218c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	switch (monitor_state) {
1268c2ecf20Sopenharmony_ci	case NCSI_CHANNEL_MONITOR_START:
1278c2ecf20Sopenharmony_ci	case NCSI_CHANNEL_MONITOR_RETRY:
1288c2ecf20Sopenharmony_ci		nca.ndp = ndp;
1298c2ecf20Sopenharmony_ci		nca.package = np->id;
1308c2ecf20Sopenharmony_ci		nca.channel = nc->id;
1318c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_GLS;
1328c2ecf20Sopenharmony_ci		nca.req_flags = 0;
1338c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
1348c2ecf20Sopenharmony_ci		if (ret)
1358c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
1368c2ecf20Sopenharmony_ci				   ret);
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	default:
1418c2ecf20Sopenharmony_ci		netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
1428c2ecf20Sopenharmony_ci			   nc->id);
1438c2ecf20Sopenharmony_ci		ncsi_report_link(ndp, true);
1448c2ecf20Sopenharmony_ci		ndp->flags |= NCSI_DEV_RESHUFFLE;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		ncm = &nc->modes[NCSI_MODE_LINK];
1478c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nc->lock, flags);
1488c2ecf20Sopenharmony_ci		nc->monitor.enabled = false;
1498c2ecf20Sopenharmony_ci		nc->state = NCSI_CHANNEL_INVISIBLE;
1508c2ecf20Sopenharmony_ci		ncm->data[2] &= ~0x1;
1518c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndp->lock, flags);
1548c2ecf20Sopenharmony_ci		nc->state = NCSI_CHANNEL_ACTIVE;
1558c2ecf20Sopenharmony_ci		list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1568c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
1578c2ecf20Sopenharmony_ci		ncsi_process_next_channel(ndp);
1588c2ecf20Sopenharmony_ci		return;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
1628c2ecf20Sopenharmony_ci	nc->monitor.state++;
1638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
1648c2ecf20Sopenharmony_ci	mod_timer(&nc->monitor.timer, jiffies + HZ);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_civoid ncsi_start_channel_monitor(struct ncsi_channel *nc)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	unsigned long flags;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
1728c2ecf20Sopenharmony_ci	WARN_ON_ONCE(nc->monitor.enabled);
1738c2ecf20Sopenharmony_ci	nc->monitor.enabled = true;
1748c2ecf20Sopenharmony_ci	nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
1758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mod_timer(&nc->monitor.timer, jiffies + HZ);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid ncsi_stop_channel_monitor(struct ncsi_channel *nc)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	unsigned long flags;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
1858c2ecf20Sopenharmony_ci	if (!nc->monitor.enabled) {
1868c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
1878c2ecf20Sopenharmony_ci		return;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	nc->monitor.enabled = false;
1908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	del_timer_sync(&nc->monitor.timer);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistruct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
1968c2ecf20Sopenharmony_ci				       unsigned char id)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_CHANNEL(np, nc) {
2018c2ecf20Sopenharmony_ci		if (nc->id == id)
2028c2ecf20Sopenharmony_ci			return nc;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return NULL;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistruct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *tmp;
2118c2ecf20Sopenharmony_ci	int index;
2128c2ecf20Sopenharmony_ci	unsigned long flags;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
2158c2ecf20Sopenharmony_ci	if (!nc)
2168c2ecf20Sopenharmony_ci		return NULL;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	nc->id = id;
2198c2ecf20Sopenharmony_ci	nc->package = np;
2208c2ecf20Sopenharmony_ci	nc->state = NCSI_CHANNEL_INACTIVE;
2218c2ecf20Sopenharmony_ci	nc->monitor.enabled = false;
2228c2ecf20Sopenharmony_ci	timer_setup(&nc->monitor.timer, ncsi_channel_monitor, 0);
2238c2ecf20Sopenharmony_ci	spin_lock_init(&nc->lock);
2248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nc->link);
2258c2ecf20Sopenharmony_ci	for (index = 0; index < NCSI_CAP_MAX; index++)
2268c2ecf20Sopenharmony_ci		nc->caps[index].index = index;
2278c2ecf20Sopenharmony_ci	for (index = 0; index < NCSI_MODE_MAX; index++)
2288c2ecf20Sopenharmony_ci		nc->modes[index].index = index;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
2318c2ecf20Sopenharmony_ci	tmp = ncsi_find_channel(np, id);
2328c2ecf20Sopenharmony_ci	if (tmp) {
2338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
2348c2ecf20Sopenharmony_ci		kfree(nc);
2358c2ecf20Sopenharmony_ci		return tmp;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	list_add_tail_rcu(&nc->node, &np->channels);
2398c2ecf20Sopenharmony_ci	np->channel_num++;
2408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return nc;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void ncsi_remove_channel(struct ncsi_channel *nc)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct ncsi_package *np = nc->package;
2488c2ecf20Sopenharmony_ci	unsigned long flags;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Release filters */
2538c2ecf20Sopenharmony_ci	kfree(nc->mac_filter.addrs);
2548c2ecf20Sopenharmony_ci	kfree(nc->vlan_filter.vids);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	nc->state = NCSI_CHANNEL_INACTIVE;
2578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
2588c2ecf20Sopenharmony_ci	ncsi_stop_channel_monitor(nc);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* Remove and free channel */
2618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
2628c2ecf20Sopenharmony_ci	list_del_rcu(&nc->node);
2638c2ecf20Sopenharmony_ci	np->channel_num--;
2648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	kfree(nc);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistruct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
2708c2ecf20Sopenharmony_ci				       unsigned char id)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct ncsi_package *np;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
2758c2ecf20Sopenharmony_ci		if (np->id == id)
2768c2ecf20Sopenharmony_ci			return np;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return NULL;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistruct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
2838c2ecf20Sopenharmony_ci				      unsigned char id)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct ncsi_package *np, *tmp;
2868c2ecf20Sopenharmony_ci	unsigned long flags;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_ATOMIC);
2898c2ecf20Sopenharmony_ci	if (!np)
2908c2ecf20Sopenharmony_ci		return NULL;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	np->id = id;
2938c2ecf20Sopenharmony_ci	np->ndp = ndp;
2948c2ecf20Sopenharmony_ci	spin_lock_init(&np->lock);
2958c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&np->channels);
2968c2ecf20Sopenharmony_ci	np->channel_whitelist = UINT_MAX;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
2998c2ecf20Sopenharmony_ci	tmp = ncsi_find_package(ndp, id);
3008c2ecf20Sopenharmony_ci	if (tmp) {
3018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
3028c2ecf20Sopenharmony_ci		kfree(np);
3038c2ecf20Sopenharmony_ci		return tmp;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	list_add_tail_rcu(&np->node, &ndp->packages);
3078c2ecf20Sopenharmony_ci	ndp->package_num++;
3088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return np;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_civoid ncsi_remove_package(struct ncsi_package *np)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = np->ndp;
3168c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *tmp;
3178c2ecf20Sopenharmony_ci	unsigned long flags;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* Release all child channels */
3208c2ecf20Sopenharmony_ci	list_for_each_entry_safe(nc, tmp, &np->channels, node)
3218c2ecf20Sopenharmony_ci		ncsi_remove_channel(nc);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* Remove and free package */
3248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
3258c2ecf20Sopenharmony_ci	list_del_rcu(&np->node);
3268c2ecf20Sopenharmony_ci	ndp->package_num--;
3278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	kfree(np);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_civoid ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
3338c2ecf20Sopenharmony_ci				   unsigned char id,
3348c2ecf20Sopenharmony_ci				   struct ncsi_package **np,
3358c2ecf20Sopenharmony_ci				   struct ncsi_channel **nc)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct ncsi_package *p;
3388c2ecf20Sopenharmony_ci	struct ncsi_channel *c;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
3418c2ecf20Sopenharmony_ci	c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (np)
3448c2ecf20Sopenharmony_ci		*np = p;
3458c2ecf20Sopenharmony_ci	if (nc)
3468c2ecf20Sopenharmony_ci		*nc = c;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/* For two consecutive NCSI commands, the packet IDs shouldn't
3508c2ecf20Sopenharmony_ci * be same. Otherwise, the bogus response might be replied. So
3518c2ecf20Sopenharmony_ci * the available IDs are allocated in round-robin fashion.
3528c2ecf20Sopenharmony_ci */
3538c2ecf20Sopenharmony_cistruct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
3548c2ecf20Sopenharmony_ci					unsigned int req_flags)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct ncsi_request *nr = NULL;
3578c2ecf20Sopenharmony_ci	int i, limit = ARRAY_SIZE(ndp->requests);
3588c2ecf20Sopenharmony_ci	unsigned long flags;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Check if there is one available request until the ceiling */
3618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
3628c2ecf20Sopenharmony_ci	for (i = ndp->request_id; i < limit; i++) {
3638c2ecf20Sopenharmony_ci		if (ndp->requests[i].used)
3648c2ecf20Sopenharmony_ci			continue;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		nr = &ndp->requests[i];
3678c2ecf20Sopenharmony_ci		nr->used = true;
3688c2ecf20Sopenharmony_ci		nr->flags = req_flags;
3698c2ecf20Sopenharmony_ci		ndp->request_id = i + 1;
3708c2ecf20Sopenharmony_ci		goto found;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Fail back to check from the starting cursor */
3748c2ecf20Sopenharmony_ci	for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
3758c2ecf20Sopenharmony_ci		if (ndp->requests[i].used)
3768c2ecf20Sopenharmony_ci			continue;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		nr = &ndp->requests[i];
3798c2ecf20Sopenharmony_ci		nr->used = true;
3808c2ecf20Sopenharmony_ci		nr->flags = req_flags;
3818c2ecf20Sopenharmony_ci		ndp->request_id = i + 1;
3828c2ecf20Sopenharmony_ci		goto found;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cifound:
3868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
3878c2ecf20Sopenharmony_ci	return nr;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_civoid ncsi_free_request(struct ncsi_request *nr)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = nr->ndp;
3938c2ecf20Sopenharmony_ci	struct sk_buff *cmd, *rsp;
3948c2ecf20Sopenharmony_ci	unsigned long flags;
3958c2ecf20Sopenharmony_ci	bool driven;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (nr->enabled) {
3988c2ecf20Sopenharmony_ci		nr->enabled = false;
3998c2ecf20Sopenharmony_ci		del_timer_sync(&nr->timer);
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
4038c2ecf20Sopenharmony_ci	cmd = nr->cmd;
4048c2ecf20Sopenharmony_ci	rsp = nr->rsp;
4058c2ecf20Sopenharmony_ci	nr->cmd = NULL;
4068c2ecf20Sopenharmony_ci	nr->rsp = NULL;
4078c2ecf20Sopenharmony_ci	nr->used = false;
4088c2ecf20Sopenharmony_ci	driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
4098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (driven && cmd && --ndp->pending_req_num == 0)
4128c2ecf20Sopenharmony_ci		schedule_work(&ndp->work);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Release command and response */
4158c2ecf20Sopenharmony_ci	consume_skb(cmd);
4168c2ecf20Sopenharmony_ci	consume_skb(rsp);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistruct ncsi_dev *ncsi_find_dev(struct net_device *dev)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_DEV(ndp) {
4248c2ecf20Sopenharmony_ci		if (ndp->ndev.dev == dev)
4258c2ecf20Sopenharmony_ci			return &ndp->ndev;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return NULL;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void ncsi_request_timeout(struct timer_list *t)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct ncsi_request *nr = from_timer(nr, t, timer);
4348c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = nr->ndp;
4358c2ecf20Sopenharmony_ci	struct ncsi_cmd_pkt *cmd;
4368c2ecf20Sopenharmony_ci	struct ncsi_package *np;
4378c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
4388c2ecf20Sopenharmony_ci	unsigned long flags;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* If the request already had associated response,
4418c2ecf20Sopenharmony_ci	 * let the response handler to release it.
4428c2ecf20Sopenharmony_ci	 */
4438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
4448c2ecf20Sopenharmony_ci	nr->enabled = false;
4458c2ecf20Sopenharmony_ci	if (nr->rsp || !nr->cmd) {
4468c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
4478c2ecf20Sopenharmony_ci		return;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
4528c2ecf20Sopenharmony_ci		if (nr->cmd) {
4538c2ecf20Sopenharmony_ci			/* Find the package */
4548c2ecf20Sopenharmony_ci			cmd = (struct ncsi_cmd_pkt *)
4558c2ecf20Sopenharmony_ci			      skb_network_header(nr->cmd);
4568c2ecf20Sopenharmony_ci			ncsi_find_package_and_channel(ndp,
4578c2ecf20Sopenharmony_ci						      cmd->cmd.common.channel,
4588c2ecf20Sopenharmony_ci						      &np, &nc);
4598c2ecf20Sopenharmony_ci			ncsi_send_netlink_timeout(nr, np, nc);
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Release the request */
4648c2ecf20Sopenharmony_ci	ncsi_free_request(nr);
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
4708c2ecf20Sopenharmony_ci	struct ncsi_package *np;
4718c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *tmp;
4728c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
4738c2ecf20Sopenharmony_ci	unsigned long flags;
4748c2ecf20Sopenharmony_ci	int ret;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	np = ndp->active_package;
4778c2ecf20Sopenharmony_ci	nc = ndp->active_channel;
4788c2ecf20Sopenharmony_ci	nca.ndp = ndp;
4798c2ecf20Sopenharmony_ci	nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
4808c2ecf20Sopenharmony_ci	switch (nd->state) {
4818c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend:
4828c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_suspend_select;
4838c2ecf20Sopenharmony_ci		fallthrough;
4848c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_select:
4858c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_SP;
4888c2ecf20Sopenharmony_ci		nca.package = np->id;
4898c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
4908c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_HWA)
4918c2ecf20Sopenharmony_ci			nca.bytes[0] = 0;
4928c2ecf20Sopenharmony_ci		else
4938c2ecf20Sopenharmony_ci			nca.bytes[0] = 1;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* To retrieve the last link states of channels in current
4968c2ecf20Sopenharmony_ci		 * package when current active channel needs fail over to
4978c2ecf20Sopenharmony_ci		 * another one. It means we will possibly select another
4988c2ecf20Sopenharmony_ci		 * channel as next active one. The link states of channels
4998c2ecf20Sopenharmony_ci		 * are most important factor of the selection. So we need
5008c2ecf20Sopenharmony_ci		 * accurate link states. Unfortunately, the link states on
5018c2ecf20Sopenharmony_ci		 * inactive channels can't be updated with LSC AEN in time.
5028c2ecf20Sopenharmony_ci		 */
5038c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_RESHUFFLE)
5048c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_suspend_gls;
5058c2ecf20Sopenharmony_ci		else
5068c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_suspend_dcnt;
5078c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
5088c2ecf20Sopenharmony_ci		if (ret)
5098c2ecf20Sopenharmony_ci			goto error;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		break;
5128c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_gls:
5138c2ecf20Sopenharmony_ci		ndp->pending_req_num = np->channel_num;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_GLS;
5168c2ecf20Sopenharmony_ci		nca.package = np->id;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_suspend_dcnt;
5198c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
5208c2ecf20Sopenharmony_ci			nca.channel = nc->id;
5218c2ecf20Sopenharmony_ci			ret = ncsi_xmit_cmd(&nca);
5228c2ecf20Sopenharmony_ci			if (ret)
5238c2ecf20Sopenharmony_ci				goto error;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		break;
5278c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_dcnt:
5288c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DCNT;
5318c2ecf20Sopenharmony_ci		nca.package = np->id;
5328c2ecf20Sopenharmony_ci		nca.channel = nc->id;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_suspend_dc;
5358c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
5368c2ecf20Sopenharmony_ci		if (ret)
5378c2ecf20Sopenharmony_ci			goto error;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_dc:
5418c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DC;
5448c2ecf20Sopenharmony_ci		nca.package = np->id;
5458c2ecf20Sopenharmony_ci		nca.channel = nc->id;
5468c2ecf20Sopenharmony_ci		nca.bytes[0] = 1;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_suspend_deselect;
5498c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
5508c2ecf20Sopenharmony_ci		if (ret)
5518c2ecf20Sopenharmony_ci			goto error;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, tmp) {
5548c2ecf20Sopenharmony_ci			/* If there is another channel active on this package
5558c2ecf20Sopenharmony_ci			 * do not deselect the package.
5568c2ecf20Sopenharmony_ci			 */
5578c2ecf20Sopenharmony_ci			if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
5588c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_suspend_done;
5598c2ecf20Sopenharmony_ci				break;
5608c2ecf20Sopenharmony_ci			}
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci		break;
5638c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_deselect:
5648c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DP;
5678c2ecf20Sopenharmony_ci		nca.package = np->id;
5688c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_suspend_done;
5718c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
5728c2ecf20Sopenharmony_ci		if (ret)
5738c2ecf20Sopenharmony_ci			goto error;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		break;
5768c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend_done:
5778c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nc->lock, flags);
5788c2ecf20Sopenharmony_ci		nc->state = NCSI_CHANNEL_INACTIVE;
5798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
5808c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_RESET)
5818c2ecf20Sopenharmony_ci			ncsi_reset_dev(nd);
5828c2ecf20Sopenharmony_ci		else
5838c2ecf20Sopenharmony_ci			ncsi_process_next_channel(ndp);
5848c2ecf20Sopenharmony_ci		break;
5858c2ecf20Sopenharmony_ci	default:
5868c2ecf20Sopenharmony_ci		netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
5878c2ecf20Sopenharmony_ci			    nd->state);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return;
5918c2ecf20Sopenharmony_cierror:
5928c2ecf20Sopenharmony_ci	nd->state = ncsi_dev_state_functional;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/* Check the VLAN filter bitmap for a set filter, and construct a
5968c2ecf20Sopenharmony_ci * "Set VLAN Filter - Disable" packet if found.
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_cistatic int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
5998c2ecf20Sopenharmony_ci			 struct ncsi_cmd_arg *nca)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct ncsi_channel_vlan_filter *ncf;
6028c2ecf20Sopenharmony_ci	unsigned long flags;
6038c2ecf20Sopenharmony_ci	void *bitmap;
6048c2ecf20Sopenharmony_ci	int index;
6058c2ecf20Sopenharmony_ci	u16 vid;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	ncf = &nc->vlan_filter;
6088c2ecf20Sopenharmony_ci	bitmap = &ncf->bitmap;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
6118c2ecf20Sopenharmony_ci	index = find_next_bit(bitmap, ncf->n_vids, 0);
6128c2ecf20Sopenharmony_ci	if (index >= ncf->n_vids) {
6138c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
6148c2ecf20Sopenharmony_ci		return -1;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci	vid = ncf->vids[index];
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	clear_bit(index, bitmap);
6198c2ecf20Sopenharmony_ci	ncf->vids[index] = 0;
6208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	nca->type = NCSI_PKT_CMD_SVF;
6238c2ecf20Sopenharmony_ci	nca->words[1] = vid;
6248c2ecf20Sopenharmony_ci	/* HW filter index starts at 1 */
6258c2ecf20Sopenharmony_ci	nca->bytes[6] = index + 1;
6268c2ecf20Sopenharmony_ci	nca->bytes[7] = 0x00;
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
6318c2ecf20Sopenharmony_ci * packet.
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_cistatic int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
6348c2ecf20Sopenharmony_ci		       struct ncsi_cmd_arg *nca)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct ncsi_channel_vlan_filter *ncf;
6378c2ecf20Sopenharmony_ci	struct vlan_vid *vlan = NULL;
6388c2ecf20Sopenharmony_ci	unsigned long flags;
6398c2ecf20Sopenharmony_ci	int i, index;
6408c2ecf20Sopenharmony_ci	void *bitmap;
6418c2ecf20Sopenharmony_ci	u16 vid;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (list_empty(&ndp->vlan_vids))
6448c2ecf20Sopenharmony_ci		return -1;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ncf = &nc->vlan_filter;
6478c2ecf20Sopenharmony_ci	bitmap = &ncf->bitmap;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	rcu_read_lock();
6528c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
6538c2ecf20Sopenharmony_ci		vid = vlan->vid;
6548c2ecf20Sopenharmony_ci		for (i = 0; i < ncf->n_vids; i++)
6558c2ecf20Sopenharmony_ci			if (ncf->vids[i] == vid) {
6568c2ecf20Sopenharmony_ci				vid = 0;
6578c2ecf20Sopenharmony_ci				break;
6588c2ecf20Sopenharmony_ci			}
6598c2ecf20Sopenharmony_ci		if (vid)
6608c2ecf20Sopenharmony_ci			break;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci	rcu_read_unlock();
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (!vid) {
6658c2ecf20Sopenharmony_ci		/* No VLAN ID is not set */
6668c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
6678c2ecf20Sopenharmony_ci		return -1;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
6718c2ecf20Sopenharmony_ci	if (index < 0 || index >= ncf->n_vids) {
6728c2ecf20Sopenharmony_ci		netdev_err(ndp->ndev.dev,
6738c2ecf20Sopenharmony_ci			   "Channel %u already has all VLAN filters set\n",
6748c2ecf20Sopenharmony_ci			   nc->id);
6758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
6768c2ecf20Sopenharmony_ci		return -1;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	ncf->vids[index] = vid;
6808c2ecf20Sopenharmony_ci	set_bit(index, bitmap);
6818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	nca->type = NCSI_PKT_CMD_SVF;
6848c2ecf20Sopenharmony_ci	nca->words[1] = vid;
6858c2ecf20Sopenharmony_ci	/* HW filter index starts at 1 */
6868c2ecf20Sopenharmony_ci	nca->bytes[6] = index + 1;
6878c2ecf20Sopenharmony_ci	nca->bytes[7] = 0x01;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return 0;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci/* NCSI OEM Command APIs */
6958c2ecf20Sopenharmony_cistatic int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	unsigned char data[NCSI_OEM_BCM_CMD_GMA_LEN];
6988c2ecf20Sopenharmony_ci	int ret = 0;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	nca->payload = NCSI_OEM_BCM_CMD_GMA_LEN;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	memset(data, 0, NCSI_OEM_BCM_CMD_GMA_LEN);
7038c2ecf20Sopenharmony_ci	*(unsigned int *)data = ntohl(NCSI_OEM_MFR_BCM_ID);
7048c2ecf20Sopenharmony_ci	data[5] = NCSI_OEM_BCM_CMD_GMA;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	nca->data = data;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ret = ncsi_xmit_cmd(nca);
7098c2ecf20Sopenharmony_ci	if (ret)
7108c2ecf20Sopenharmony_ci		netdev_err(nca->ndp->ndev.dev,
7118c2ecf20Sopenharmony_ci			   "NCSI: Failed to transmit cmd 0x%x during configure\n",
7128c2ecf20Sopenharmony_ci			   nca->type);
7138c2ecf20Sopenharmony_ci	return ret;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int ncsi_oem_gma_handler_mlx(struct ncsi_cmd_arg *nca)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	union {
7198c2ecf20Sopenharmony_ci		u8 data_u8[NCSI_OEM_MLX_CMD_GMA_LEN];
7208c2ecf20Sopenharmony_ci		u32 data_u32[NCSI_OEM_MLX_CMD_GMA_LEN / sizeof(u32)];
7218c2ecf20Sopenharmony_ci	} u;
7228c2ecf20Sopenharmony_ci	int ret = 0;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	nca->payload = NCSI_OEM_MLX_CMD_GMA_LEN;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	memset(&u, 0, sizeof(u));
7278c2ecf20Sopenharmony_ci	u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
7288c2ecf20Sopenharmony_ci	u.data_u8[5] = NCSI_OEM_MLX_CMD_GMA;
7298c2ecf20Sopenharmony_ci	u.data_u8[6] = NCSI_OEM_MLX_CMD_GMA_PARAM;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	nca->data = u.data_u8;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	ret = ncsi_xmit_cmd(nca);
7348c2ecf20Sopenharmony_ci	if (ret)
7358c2ecf20Sopenharmony_ci		netdev_err(nca->ndp->ndev.dev,
7368c2ecf20Sopenharmony_ci			   "NCSI: Failed to transmit cmd 0x%x during configure\n",
7378c2ecf20Sopenharmony_ci			   nca->type);
7388c2ecf20Sopenharmony_ci	return ret;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic int ncsi_oem_smaf_mlx(struct ncsi_cmd_arg *nca)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	union {
7448c2ecf20Sopenharmony_ci		u8 data_u8[NCSI_OEM_MLX_CMD_SMAF_LEN];
7458c2ecf20Sopenharmony_ci		u32 data_u32[NCSI_OEM_MLX_CMD_SMAF_LEN / sizeof(u32)];
7468c2ecf20Sopenharmony_ci	} u;
7478c2ecf20Sopenharmony_ci	int ret = 0;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	memset(&u, 0, sizeof(u));
7508c2ecf20Sopenharmony_ci	u.data_u32[0] = ntohl(NCSI_OEM_MFR_MLX_ID);
7518c2ecf20Sopenharmony_ci	u.data_u8[5] = NCSI_OEM_MLX_CMD_SMAF;
7528c2ecf20Sopenharmony_ci	u.data_u8[6] = NCSI_OEM_MLX_CMD_SMAF_PARAM;
7538c2ecf20Sopenharmony_ci	memcpy(&u.data_u8[MLX_SMAF_MAC_ADDR_OFFSET],
7548c2ecf20Sopenharmony_ci	       nca->ndp->ndev.dev->dev_addr,	ETH_ALEN);
7558c2ecf20Sopenharmony_ci	u.data_u8[MLX_SMAF_MED_SUPPORT_OFFSET] =
7568c2ecf20Sopenharmony_ci		(MLX_MC_RBT_AVL | MLX_MC_RBT_SUPPORT);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	nca->payload = NCSI_OEM_MLX_CMD_SMAF_LEN;
7598c2ecf20Sopenharmony_ci	nca->data = u.data_u8;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	ret = ncsi_xmit_cmd(nca);
7628c2ecf20Sopenharmony_ci	if (ret)
7638c2ecf20Sopenharmony_ci		netdev_err(nca->ndp->ndev.dev,
7648c2ecf20Sopenharmony_ci			   "NCSI: Failed to transmit cmd 0x%x during probe\n",
7658c2ecf20Sopenharmony_ci			   nca->type);
7668c2ecf20Sopenharmony_ci	return ret;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci/* OEM Command handlers initialization */
7708c2ecf20Sopenharmony_cistatic struct ncsi_oem_gma_handler {
7718c2ecf20Sopenharmony_ci	unsigned int	mfr_id;
7728c2ecf20Sopenharmony_ci	int		(*handler)(struct ncsi_cmd_arg *nca);
7738c2ecf20Sopenharmony_ci} ncsi_oem_gma_handlers[] = {
7748c2ecf20Sopenharmony_ci	{ NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm },
7758c2ecf20Sopenharmony_ci	{ NCSI_OEM_MFR_MLX_ID, ncsi_oem_gma_handler_mlx }
7768c2ecf20Sopenharmony_ci};
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
7798c2ecf20Sopenharmony_ci{
7808c2ecf20Sopenharmony_ci	struct ncsi_oem_gma_handler *nch = NULL;
7818c2ecf20Sopenharmony_ci	int i;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* This function should only be called once, return if flag set */
7848c2ecf20Sopenharmony_ci	if (nca->ndp->gma_flag == 1)
7858c2ecf20Sopenharmony_ci		return -1;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* Find gma handler for given manufacturer id */
7888c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) {
7898c2ecf20Sopenharmony_ci		if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) {
7908c2ecf20Sopenharmony_ci			if (ncsi_oem_gma_handlers[i].handler)
7918c2ecf20Sopenharmony_ci				nch = &ncsi_oem_gma_handlers[i];
7928c2ecf20Sopenharmony_ci			break;
7938c2ecf20Sopenharmony_ci			}
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (!nch) {
7978c2ecf20Sopenharmony_ci		netdev_err(nca->ndp->ndev.dev,
7988c2ecf20Sopenharmony_ci			   "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
7998c2ecf20Sopenharmony_ci			   mf_id);
8008c2ecf20Sopenharmony_ci		return -1;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* Get Mac address from NCSI device */
8048c2ecf20Sopenharmony_ci	return nch->handler(nca);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci/* Determine if a given channel from the channel_queue should be used for Tx */
8108c2ecf20Sopenharmony_cistatic bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp,
8118c2ecf20Sopenharmony_ci			       struct ncsi_channel *nc)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct ncsi_channel_mode *ncm;
8148c2ecf20Sopenharmony_ci	struct ncsi_channel *channel;
8158c2ecf20Sopenharmony_ci	struct ncsi_package *np;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* Check if any other channel has Tx enabled; a channel may have already
8188c2ecf20Sopenharmony_ci	 * been configured and removed from the channel queue.
8198c2ecf20Sopenharmony_ci	 */
8208c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
8218c2ecf20Sopenharmony_ci		if (!ndp->multi_package && np != nc->package)
8228c2ecf20Sopenharmony_ci			continue;
8238c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, channel) {
8248c2ecf20Sopenharmony_ci			ncm = &channel->modes[NCSI_MODE_TX_ENABLE];
8258c2ecf20Sopenharmony_ci			if (ncm->enable)
8268c2ecf20Sopenharmony_ci				return false;
8278c2ecf20Sopenharmony_ci		}
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* This channel is the preferred channel and has link */
8318c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
8328c2ecf20Sopenharmony_ci		np = channel->package;
8338c2ecf20Sopenharmony_ci		if (np->preferred_channel &&
8348c2ecf20Sopenharmony_ci		    ncsi_channel_has_link(np->preferred_channel)) {
8358c2ecf20Sopenharmony_ci			return np->preferred_channel == nc;
8368c2ecf20Sopenharmony_ci		}
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* This channel has link */
8408c2ecf20Sopenharmony_ci	if (ncsi_channel_has_link(nc))
8418c2ecf20Sopenharmony_ci		return true;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(channel, &ndp->channel_queue, link)
8448c2ecf20Sopenharmony_ci		if (ncsi_channel_has_link(channel))
8458c2ecf20Sopenharmony_ci			return false;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* No other channel has link; default to this one */
8488c2ecf20Sopenharmony_ci	return true;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/* Change the active Tx channel in a multi-channel setup */
8528c2ecf20Sopenharmony_ciint ncsi_update_tx_channel(struct ncsi_dev_priv *ndp,
8538c2ecf20Sopenharmony_ci			   struct ncsi_package *package,
8548c2ecf20Sopenharmony_ci			   struct ncsi_channel *disable,
8558c2ecf20Sopenharmony_ci			   struct ncsi_channel *enable)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
8588c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
8598c2ecf20Sopenharmony_ci	struct ncsi_package *np;
8608c2ecf20Sopenharmony_ci	int ret = 0;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (!package->multi_channel && !ndp->multi_package)
8638c2ecf20Sopenharmony_ci		netdev_warn(ndp->ndev.dev,
8648c2ecf20Sopenharmony_ci			    "NCSI: Trying to update Tx channel in single-channel mode\n");
8658c2ecf20Sopenharmony_ci	nca.ndp = ndp;
8668c2ecf20Sopenharmony_ci	nca.req_flags = 0;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* Find current channel with Tx enabled */
8698c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
8708c2ecf20Sopenharmony_ci		if (disable)
8718c2ecf20Sopenharmony_ci			break;
8728c2ecf20Sopenharmony_ci		if (!ndp->multi_package && np != package)
8738c2ecf20Sopenharmony_ci			continue;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc)
8768c2ecf20Sopenharmony_ci			if (nc->modes[NCSI_MODE_TX_ENABLE].enable) {
8778c2ecf20Sopenharmony_ci				disable = nc;
8788c2ecf20Sopenharmony_ci				break;
8798c2ecf20Sopenharmony_ci			}
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* Find a suitable channel for Tx */
8838c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
8848c2ecf20Sopenharmony_ci		if (enable)
8858c2ecf20Sopenharmony_ci			break;
8868c2ecf20Sopenharmony_ci		if (!ndp->multi_package && np != package)
8878c2ecf20Sopenharmony_ci			continue;
8888c2ecf20Sopenharmony_ci		if (!(ndp->package_whitelist & (0x1 << np->id)))
8898c2ecf20Sopenharmony_ci			continue;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci		if (np->preferred_channel &&
8928c2ecf20Sopenharmony_ci		    ncsi_channel_has_link(np->preferred_channel)) {
8938c2ecf20Sopenharmony_ci			enable = np->preferred_channel;
8948c2ecf20Sopenharmony_ci			break;
8958c2ecf20Sopenharmony_ci		}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
8988c2ecf20Sopenharmony_ci			if (!(np->channel_whitelist & 0x1 << nc->id))
8998c2ecf20Sopenharmony_ci				continue;
9008c2ecf20Sopenharmony_ci			if (nc->state != NCSI_CHANNEL_ACTIVE)
9018c2ecf20Sopenharmony_ci				continue;
9028c2ecf20Sopenharmony_ci			if (ncsi_channel_has_link(nc)) {
9038c2ecf20Sopenharmony_ci				enable = nc;
9048c2ecf20Sopenharmony_ci				break;
9058c2ecf20Sopenharmony_ci			}
9068c2ecf20Sopenharmony_ci		}
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (disable == enable)
9108c2ecf20Sopenharmony_ci		return -1;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if (!enable)
9138c2ecf20Sopenharmony_ci		return -1;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (disable) {
9168c2ecf20Sopenharmony_ci		nca.channel = disable->id;
9178c2ecf20Sopenharmony_ci		nca.package = disable->package->id;
9188c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DCNT;
9198c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
9208c2ecf20Sopenharmony_ci		if (ret)
9218c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev,
9228c2ecf20Sopenharmony_ci				   "Error %d sending DCNT\n",
9238c2ecf20Sopenharmony_ci				   ret);
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	nca.channel = enable->id;
9298c2ecf20Sopenharmony_ci	nca.package = enable->package->id;
9308c2ecf20Sopenharmony_ci	nca.type = NCSI_PKT_CMD_ECNT;
9318c2ecf20Sopenharmony_ci	ret = ncsi_xmit_cmd(&nca);
9328c2ecf20Sopenharmony_ci	if (ret)
9338c2ecf20Sopenharmony_ci		netdev_err(ndp->ndev.dev,
9348c2ecf20Sopenharmony_ci			   "Error %d sending ECNT\n",
9358c2ecf20Sopenharmony_ci			   ret);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	return ret;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_cistatic void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	struct ncsi_package *np = ndp->active_package;
9438c2ecf20Sopenharmony_ci	struct ncsi_channel *nc = ndp->active_channel;
9448c2ecf20Sopenharmony_ci	struct ncsi_channel *hot_nc = NULL;
9458c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
9468c2ecf20Sopenharmony_ci	struct net_device *dev = nd->dev;
9478c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
9488c2ecf20Sopenharmony_ci	unsigned char index;
9498c2ecf20Sopenharmony_ci	unsigned long flags;
9508c2ecf20Sopenharmony_ci	int ret;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	nca.ndp = ndp;
9538c2ecf20Sopenharmony_ci	nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
9548c2ecf20Sopenharmony_ci	switch (nd->state) {
9558c2ecf20Sopenharmony_ci	case ncsi_dev_state_config:
9568c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_sp:
9578c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		/* Select the specific package */
9608c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_SP;
9618c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_HWA)
9628c2ecf20Sopenharmony_ci			nca.bytes[0] = 0;
9638c2ecf20Sopenharmony_ci		else
9648c2ecf20Sopenharmony_ci			nca.bytes[0] = 1;
9658c2ecf20Sopenharmony_ci		nca.package = np->id;
9668c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
9678c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
9688c2ecf20Sopenharmony_ci		if (ret) {
9698c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev,
9708c2ecf20Sopenharmony_ci				   "NCSI: Failed to transmit CMD_SP\n");
9718c2ecf20Sopenharmony_ci			goto error;
9728c2ecf20Sopenharmony_ci		}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_config_cis;
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_cis:
9778c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci		/* Clear initial state */
9808c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_CIS;
9818c2ecf20Sopenharmony_ci		nca.package = np->id;
9828c2ecf20Sopenharmony_ci		nca.channel = nc->id;
9838c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
9848c2ecf20Sopenharmony_ci		if (ret) {
9858c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev,
9868c2ecf20Sopenharmony_ci				   "NCSI: Failed to transmit CMD_CIS\n");
9878c2ecf20Sopenharmony_ci			goto error;
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_config_oem_gma;
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_oem_gma:
9938c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_config_clear_vids;
9948c2ecf20Sopenharmony_ci		ret = -1;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
9978c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_OEM;
9988c2ecf20Sopenharmony_ci		nca.package = np->id;
9998c2ecf20Sopenharmony_ci		nca.channel = nc->id;
10008c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
10018c2ecf20Sopenharmony_ci		ret = ncsi_gma_handler(&nca, nc->version.mf_id);
10028c2ecf20Sopenharmony_ci#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		if (ret < 0)
10058c2ecf20Sopenharmony_ci			schedule_work(&ndp->work);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		break;
10088c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_clear_vids:
10098c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_svf:
10108c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_ev:
10118c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_sma:
10128c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_ebf:
10138c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_dgmf:
10148c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_ecnt:
10158c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_ec:
10168c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_ae:
10178c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_gls:
10188c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		nca.package = np->id;
10218c2ecf20Sopenharmony_ci		nca.channel = nc->id;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		/* Clear any active filters on the channel before setting */
10248c2ecf20Sopenharmony_ci		if (nd->state == ncsi_dev_state_config_clear_vids) {
10258c2ecf20Sopenharmony_ci			ret = clear_one_vid(ndp, nc, &nca);
10268c2ecf20Sopenharmony_ci			if (ret) {
10278c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_svf;
10288c2ecf20Sopenharmony_ci				schedule_work(&ndp->work);
10298c2ecf20Sopenharmony_ci				break;
10308c2ecf20Sopenharmony_ci			}
10318c2ecf20Sopenharmony_ci			/* Repeat */
10328c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_clear_vids;
10338c2ecf20Sopenharmony_ci		/* Add known VLAN tags to the filter */
10348c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_svf) {
10358c2ecf20Sopenharmony_ci			ret = set_one_vid(ndp, nc, &nca);
10368c2ecf20Sopenharmony_ci			if (ret) {
10378c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_ev;
10388c2ecf20Sopenharmony_ci				schedule_work(&ndp->work);
10398c2ecf20Sopenharmony_ci				break;
10408c2ecf20Sopenharmony_ci			}
10418c2ecf20Sopenharmony_ci			/* Repeat */
10428c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_svf;
10438c2ecf20Sopenharmony_ci		/* Enable/Disable the VLAN filter */
10448c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_ev) {
10458c2ecf20Sopenharmony_ci			if (list_empty(&ndp->vlan_vids)) {
10468c2ecf20Sopenharmony_ci				nca.type = NCSI_PKT_CMD_DV;
10478c2ecf20Sopenharmony_ci			} else {
10488c2ecf20Sopenharmony_ci				nca.type = NCSI_PKT_CMD_EV;
10498c2ecf20Sopenharmony_ci				nca.bytes[3] = NCSI_CAP_VLAN_NO;
10508c2ecf20Sopenharmony_ci			}
10518c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_sma;
10528c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_sma) {
10538c2ecf20Sopenharmony_ci		/* Use first entry in unicast filter table. Note that
10548c2ecf20Sopenharmony_ci		 * the MAC filter table starts from entry 1 instead of
10558c2ecf20Sopenharmony_ci		 * 0.
10568c2ecf20Sopenharmony_ci		 */
10578c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_SMA;
10588c2ecf20Sopenharmony_ci			for (index = 0; index < 6; index++)
10598c2ecf20Sopenharmony_ci				nca.bytes[index] = dev->dev_addr[index];
10608c2ecf20Sopenharmony_ci			nca.bytes[6] = 0x1;
10618c2ecf20Sopenharmony_ci			nca.bytes[7] = 0x1;
10628c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_ebf;
10638c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_ebf) {
10648c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_EBF;
10658c2ecf20Sopenharmony_ci			nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
10668c2ecf20Sopenharmony_ci			/* if multicast global filtering is supported then
10678c2ecf20Sopenharmony_ci			 * disable it so that all multicast packet will be
10688c2ecf20Sopenharmony_ci			 * forwarded to management controller
10698c2ecf20Sopenharmony_ci			 */
10708c2ecf20Sopenharmony_ci			if (nc->caps[NCSI_CAP_GENERIC].cap &
10718c2ecf20Sopenharmony_ci			    NCSI_CAP_GENERIC_MC)
10728c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_dgmf;
10738c2ecf20Sopenharmony_ci			else if (ncsi_channel_is_tx(ndp, nc))
10748c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_ecnt;
10758c2ecf20Sopenharmony_ci			else
10768c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_ec;
10778c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_dgmf) {
10788c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_DGMF;
10798c2ecf20Sopenharmony_ci			if (ncsi_channel_is_tx(ndp, nc))
10808c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_ecnt;
10818c2ecf20Sopenharmony_ci			else
10828c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_ec;
10838c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_ecnt) {
10848c2ecf20Sopenharmony_ci			if (np->preferred_channel &&
10858c2ecf20Sopenharmony_ci			    nc != np->preferred_channel)
10868c2ecf20Sopenharmony_ci				netdev_info(ndp->ndev.dev,
10878c2ecf20Sopenharmony_ci					    "NCSI: Tx failed over to channel %u\n",
10888c2ecf20Sopenharmony_ci					    nc->id);
10898c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_ECNT;
10908c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_ec;
10918c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_ec) {
10928c2ecf20Sopenharmony_ci			/* Enable AEN if it's supported */
10938c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_EC;
10948c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_ae;
10958c2ecf20Sopenharmony_ci			if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
10968c2ecf20Sopenharmony_ci				nd->state = ncsi_dev_state_config_gls;
10978c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_ae) {
10988c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_AE;
10998c2ecf20Sopenharmony_ci			nca.bytes[0] = 0;
11008c2ecf20Sopenharmony_ci			nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
11018c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_gls;
11028c2ecf20Sopenharmony_ci		} else if (nd->state == ncsi_dev_state_config_gls) {
11038c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_GLS;
11048c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_config_done;
11058c2ecf20Sopenharmony_ci		}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
11088c2ecf20Sopenharmony_ci		if (ret) {
11098c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev,
11108c2ecf20Sopenharmony_ci				   "NCSI: Failed to transmit CMD %x\n",
11118c2ecf20Sopenharmony_ci				   nca.type);
11128c2ecf20Sopenharmony_ci			goto error;
11138c2ecf20Sopenharmony_ci		}
11148c2ecf20Sopenharmony_ci		break;
11158c2ecf20Sopenharmony_ci	case ncsi_dev_state_config_done:
11168c2ecf20Sopenharmony_ci		netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
11178c2ecf20Sopenharmony_ci			   nc->id);
11188c2ecf20Sopenharmony_ci		spin_lock_irqsave(&nc->lock, flags);
11198c2ecf20Sopenharmony_ci		nc->state = NCSI_CHANNEL_ACTIVE;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_RESET) {
11228c2ecf20Sopenharmony_ci			/* A reset event happened during config, start it now */
11238c2ecf20Sopenharmony_ci			nc->reconfigure_needed = false;
11248c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
11258c2ecf20Sopenharmony_ci			ncsi_reset_dev(nd);
11268c2ecf20Sopenharmony_ci			break;
11278c2ecf20Sopenharmony_ci		}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci		if (nc->reconfigure_needed) {
11308c2ecf20Sopenharmony_ci			/* This channel's configuration has been updated
11318c2ecf20Sopenharmony_ci			 * part-way during the config state - start the
11328c2ecf20Sopenharmony_ci			 * channel configuration over
11338c2ecf20Sopenharmony_ci			 */
11348c2ecf20Sopenharmony_ci			nc->reconfigure_needed = false;
11358c2ecf20Sopenharmony_ci			nc->state = NCSI_CHANNEL_INACTIVE;
11368c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ndp->lock, flags);
11398c2ecf20Sopenharmony_ci			list_add_tail_rcu(&nc->link, &ndp->channel_queue);
11408c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ndp->lock, flags);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci			netdev_dbg(dev, "Dirty NCSI channel state reset\n");
11438c2ecf20Sopenharmony_ci			ncsi_process_next_channel(ndp);
11448c2ecf20Sopenharmony_ci			break;
11458c2ecf20Sopenharmony_ci		}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
11488c2ecf20Sopenharmony_ci			hot_nc = nc;
11498c2ecf20Sopenharmony_ci		} else {
11508c2ecf20Sopenharmony_ci			hot_nc = NULL;
11518c2ecf20Sopenharmony_ci			netdev_dbg(ndp->ndev.dev,
11528c2ecf20Sopenharmony_ci				   "NCSI: channel %u link down after config\n",
11538c2ecf20Sopenharmony_ci				   nc->id);
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&nc->lock, flags);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		/* Update the hot channel */
11588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndp->lock, flags);
11598c2ecf20Sopenharmony_ci		ndp->hot_channel = hot_nc;
11608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci		ncsi_start_channel_monitor(nc);
11638c2ecf20Sopenharmony_ci		ncsi_process_next_channel(ndp);
11648c2ecf20Sopenharmony_ci		break;
11658c2ecf20Sopenharmony_ci	default:
11668c2ecf20Sopenharmony_ci		netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
11678c2ecf20Sopenharmony_ci			     nd->state);
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	return;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cierror:
11738c2ecf20Sopenharmony_ci	ncsi_report_link(ndp, true);
11748c2ecf20Sopenharmony_ci}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *found, *hot_nc;
11798c2ecf20Sopenharmony_ci	struct ncsi_channel_mode *ncm;
11808c2ecf20Sopenharmony_ci	unsigned long flags, cflags;
11818c2ecf20Sopenharmony_ci	struct ncsi_package *np;
11828c2ecf20Sopenharmony_ci	bool with_link;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
11858c2ecf20Sopenharmony_ci	hot_nc = ndp->hot_channel;
11868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	/* By default the search is done once an inactive channel with up
11898c2ecf20Sopenharmony_ci	 * link is found, unless a preferred channel is set.
11908c2ecf20Sopenharmony_ci	 * If multi_package or multi_channel are configured all channels in the
11918c2ecf20Sopenharmony_ci	 * whitelist are added to the channel queue.
11928c2ecf20Sopenharmony_ci	 */
11938c2ecf20Sopenharmony_ci	found = NULL;
11948c2ecf20Sopenharmony_ci	with_link = false;
11958c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
11968c2ecf20Sopenharmony_ci		if (!(ndp->package_whitelist & (0x1 << np->id)))
11978c2ecf20Sopenharmony_ci			continue;
11988c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
11998c2ecf20Sopenharmony_ci			if (!(np->channel_whitelist & (0x1 << nc->id)))
12008c2ecf20Sopenharmony_ci				continue;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, cflags);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci			if (!list_empty(&nc->link) ||
12058c2ecf20Sopenharmony_ci			    nc->state != NCSI_CHANNEL_INACTIVE) {
12068c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&nc->lock, cflags);
12078c2ecf20Sopenharmony_ci				continue;
12088c2ecf20Sopenharmony_ci			}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci			if (!found)
12118c2ecf20Sopenharmony_ci				found = nc;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci			if (nc == hot_nc)
12148c2ecf20Sopenharmony_ci				found = nc;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci			ncm = &nc->modes[NCSI_MODE_LINK];
12178c2ecf20Sopenharmony_ci			if (ncm->data[2] & 0x1) {
12188c2ecf20Sopenharmony_ci				found = nc;
12198c2ecf20Sopenharmony_ci				with_link = true;
12208c2ecf20Sopenharmony_ci			}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci			/* If multi_channel is enabled configure all valid
12238c2ecf20Sopenharmony_ci			 * channels whether or not they currently have link
12248c2ecf20Sopenharmony_ci			 * so they will have AENs enabled.
12258c2ecf20Sopenharmony_ci			 */
12268c2ecf20Sopenharmony_ci			if (with_link || np->multi_channel) {
12278c2ecf20Sopenharmony_ci				spin_lock_irqsave(&ndp->lock, flags);
12288c2ecf20Sopenharmony_ci				list_add_tail_rcu(&nc->link,
12298c2ecf20Sopenharmony_ci						  &ndp->channel_queue);
12308c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&ndp->lock, flags);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci				netdev_dbg(ndp->ndev.dev,
12338c2ecf20Sopenharmony_ci					   "NCSI: Channel %u added to queue (link %s)\n",
12348c2ecf20Sopenharmony_ci					   nc->id,
12358c2ecf20Sopenharmony_ci					   ncm->data[2] & 0x1 ? "up" : "down");
12368c2ecf20Sopenharmony_ci			}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, cflags);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci			if (with_link && !np->multi_channel)
12418c2ecf20Sopenharmony_ci				break;
12428c2ecf20Sopenharmony_ci		}
12438c2ecf20Sopenharmony_ci		if (with_link && !ndp->multi_package)
12448c2ecf20Sopenharmony_ci			break;
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (list_empty(&ndp->channel_queue) && found) {
12488c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev,
12498c2ecf20Sopenharmony_ci			    "NCSI: No channel with link found, configuring channel %u\n",
12508c2ecf20Sopenharmony_ci			    found->id);
12518c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndp->lock, flags);
12528c2ecf20Sopenharmony_ci		list_add_tail_rcu(&found->link, &ndp->channel_queue);
12538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
12548c2ecf20Sopenharmony_ci	} else if (!found) {
12558c2ecf20Sopenharmony_ci		netdev_warn(ndp->ndev.dev,
12568c2ecf20Sopenharmony_ci			    "NCSI: No channel found to configure!\n");
12578c2ecf20Sopenharmony_ci		ncsi_report_link(ndp, true);
12588c2ecf20Sopenharmony_ci		return -ENODEV;
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	return ncsi_process_next_channel(ndp);
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_cistatic bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	struct ncsi_package *np;
12678c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
12688c2ecf20Sopenharmony_ci	unsigned int cap;
12698c2ecf20Sopenharmony_ci	bool has_channel = false;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	/* The hardware arbitration is disabled if any one channel
12728c2ecf20Sopenharmony_ci	 * doesn't support explicitly.
12738c2ecf20Sopenharmony_ci	 */
12748c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
12758c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
12768c2ecf20Sopenharmony_ci			has_channel = true;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci			cap = nc->caps[NCSI_CAP_GENERIC].cap;
12798c2ecf20Sopenharmony_ci			if (!(cap & NCSI_CAP_GENERIC_HWA) ||
12808c2ecf20Sopenharmony_ci			    (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
12818c2ecf20Sopenharmony_ci			    NCSI_CAP_GENERIC_HWA_SUPPORT) {
12828c2ecf20Sopenharmony_ci				ndp->flags &= ~NCSI_DEV_HWA;
12838c2ecf20Sopenharmony_ci				return false;
12848c2ecf20Sopenharmony_ci			}
12858c2ecf20Sopenharmony_ci		}
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	if (has_channel) {
12898c2ecf20Sopenharmony_ci		ndp->flags |= NCSI_DEV_HWA;
12908c2ecf20Sopenharmony_ci		return true;
12918c2ecf20Sopenharmony_ci	}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	ndp->flags &= ~NCSI_DEV_HWA;
12948c2ecf20Sopenharmony_ci	return false;
12958c2ecf20Sopenharmony_ci}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
12988c2ecf20Sopenharmony_ci{
12998c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
13008c2ecf20Sopenharmony_ci	struct ncsi_package *np;
13018c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
13028c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
13038c2ecf20Sopenharmony_ci	unsigned char index;
13048c2ecf20Sopenharmony_ci	int ret;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	nca.ndp = ndp;
13078c2ecf20Sopenharmony_ci	nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
13088c2ecf20Sopenharmony_ci	switch (nd->state) {
13098c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe:
13108c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_deselect;
13118c2ecf20Sopenharmony_ci		fallthrough;
13128c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_deselect:
13138c2ecf20Sopenharmony_ci		ndp->pending_req_num = 8;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci		/* Deselect all possible packages */
13168c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DP;
13178c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
13188c2ecf20Sopenharmony_ci		for (index = 0; index < 8; index++) {
13198c2ecf20Sopenharmony_ci			nca.package = index;
13208c2ecf20Sopenharmony_ci			ret = ncsi_xmit_cmd(&nca);
13218c2ecf20Sopenharmony_ci			if (ret)
13228c2ecf20Sopenharmony_ci				goto error;
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_package;
13268c2ecf20Sopenharmony_ci		break;
13278c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_package:
13288c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_SP;
13318c2ecf20Sopenharmony_ci		nca.bytes[0] = 1;
13328c2ecf20Sopenharmony_ci		nca.package = ndp->package_probe_id;
13338c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
13348c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
13358c2ecf20Sopenharmony_ci		if (ret)
13368c2ecf20Sopenharmony_ci			goto error;
13378c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_channel;
13388c2ecf20Sopenharmony_ci		break;
13398c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_channel:
13408c2ecf20Sopenharmony_ci		ndp->active_package = ncsi_find_package(ndp,
13418c2ecf20Sopenharmony_ci							ndp->package_probe_id);
13428c2ecf20Sopenharmony_ci		if (!ndp->active_package) {
13438c2ecf20Sopenharmony_ci			/* No response */
13448c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_probe_dp;
13458c2ecf20Sopenharmony_ci			schedule_work(&ndp->work);
13468c2ecf20Sopenharmony_ci			break;
13478c2ecf20Sopenharmony_ci		}
13488c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_cis;
13498c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) &&
13508c2ecf20Sopenharmony_ci		    ndp->mlx_multi_host)
13518c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_probe_mlx_gma;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci		schedule_work(&ndp->work);
13548c2ecf20Sopenharmony_ci		break;
13558c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
13568c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_mlx_gma:
13578c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_OEM;
13608c2ecf20Sopenharmony_ci		nca.package = ndp->active_package->id;
13618c2ecf20Sopenharmony_ci		nca.channel = 0;
13628c2ecf20Sopenharmony_ci		ret = ncsi_oem_gma_handler_mlx(&nca);
13638c2ecf20Sopenharmony_ci		if (ret)
13648c2ecf20Sopenharmony_ci			goto error;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_mlx_smaf;
13678c2ecf20Sopenharmony_ci		break;
13688c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_mlx_smaf:
13698c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_OEM;
13728c2ecf20Sopenharmony_ci		nca.package = ndp->active_package->id;
13738c2ecf20Sopenharmony_ci		nca.channel = 0;
13748c2ecf20Sopenharmony_ci		ret = ncsi_oem_smaf_mlx(&nca);
13758c2ecf20Sopenharmony_ci		if (ret)
13768c2ecf20Sopenharmony_ci			goto error;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_cis;
13798c2ecf20Sopenharmony_ci		break;
13808c2ecf20Sopenharmony_ci#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
13818c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_cis:
13828c2ecf20Sopenharmony_ci		ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci		/* Clear initial state */
13858c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_CIS;
13868c2ecf20Sopenharmony_ci		nca.package = ndp->active_package->id;
13878c2ecf20Sopenharmony_ci		for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
13888c2ecf20Sopenharmony_ci			nca.channel = index;
13898c2ecf20Sopenharmony_ci			ret = ncsi_xmit_cmd(&nca);
13908c2ecf20Sopenharmony_ci			if (ret)
13918c2ecf20Sopenharmony_ci				goto error;
13928c2ecf20Sopenharmony_ci		}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_gvi;
13958c2ecf20Sopenharmony_ci		break;
13968c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_gvi:
13978c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_gc:
13988c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_gls:
13998c2ecf20Sopenharmony_ci		np = ndp->active_package;
14008c2ecf20Sopenharmony_ci		ndp->pending_req_num = np->channel_num;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci		/* Retrieve version, capability or link status */
14038c2ecf20Sopenharmony_ci		if (nd->state == ncsi_dev_state_probe_gvi)
14048c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_GVI;
14058c2ecf20Sopenharmony_ci		else if (nd->state == ncsi_dev_state_probe_gc)
14068c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_GC;
14078c2ecf20Sopenharmony_ci		else
14088c2ecf20Sopenharmony_ci			nca.type = NCSI_PKT_CMD_GLS;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		nca.package = np->id;
14118c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
14128c2ecf20Sopenharmony_ci			nca.channel = nc->id;
14138c2ecf20Sopenharmony_ci			ret = ncsi_xmit_cmd(&nca);
14148c2ecf20Sopenharmony_ci			if (ret)
14158c2ecf20Sopenharmony_ci				goto error;
14168c2ecf20Sopenharmony_ci		}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci		if (nd->state == ncsi_dev_state_probe_gvi)
14198c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_probe_gc;
14208c2ecf20Sopenharmony_ci		else if (nd->state == ncsi_dev_state_probe_gc)
14218c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_probe_gls;
14228c2ecf20Sopenharmony_ci		else
14238c2ecf20Sopenharmony_ci			nd->state = ncsi_dev_state_probe_dp;
14248c2ecf20Sopenharmony_ci		break;
14258c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe_dp:
14268c2ecf20Sopenharmony_ci		ndp->pending_req_num = 1;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci		/* Deselect the current package */
14298c2ecf20Sopenharmony_ci		nca.type = NCSI_PKT_CMD_DP;
14308c2ecf20Sopenharmony_ci		nca.package = ndp->package_probe_id;
14318c2ecf20Sopenharmony_ci		nca.channel = NCSI_RESERVED_CHANNEL;
14328c2ecf20Sopenharmony_ci		ret = ncsi_xmit_cmd(&nca);
14338c2ecf20Sopenharmony_ci		if (ret)
14348c2ecf20Sopenharmony_ci			goto error;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci		/* Probe next package */
14378c2ecf20Sopenharmony_ci		ndp->package_probe_id++;
14388c2ecf20Sopenharmony_ci		if (ndp->package_probe_id >= 8) {
14398c2ecf20Sopenharmony_ci			/* Probe finished */
14408c2ecf20Sopenharmony_ci			ndp->flags |= NCSI_DEV_PROBED;
14418c2ecf20Sopenharmony_ci			break;
14428c2ecf20Sopenharmony_ci		}
14438c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe_package;
14448c2ecf20Sopenharmony_ci		ndp->active_package = NULL;
14458c2ecf20Sopenharmony_ci		break;
14468c2ecf20Sopenharmony_ci	default:
14478c2ecf20Sopenharmony_ci		netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
14488c2ecf20Sopenharmony_ci			    nd->state);
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	if (ndp->flags & NCSI_DEV_PROBED) {
14528c2ecf20Sopenharmony_ci		/* Check if all packages have HWA support */
14538c2ecf20Sopenharmony_ci		ncsi_check_hwa(ndp);
14548c2ecf20Sopenharmony_ci		ncsi_choose_active_channel(ndp);
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	return;
14588c2ecf20Sopenharmony_cierror:
14598c2ecf20Sopenharmony_ci	netdev_err(ndp->ndev.dev,
14608c2ecf20Sopenharmony_ci		   "NCSI: Failed to transmit cmd 0x%x during probe\n",
14618c2ecf20Sopenharmony_ci		   nca.type);
14628c2ecf20Sopenharmony_ci	ncsi_report_link(ndp, true);
14638c2ecf20Sopenharmony_ci}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_cistatic void ncsi_dev_work(struct work_struct *work)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = container_of(work,
14688c2ecf20Sopenharmony_ci			struct ncsi_dev_priv, work);
14698c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	switch (nd->state & ncsi_dev_state_major) {
14728c2ecf20Sopenharmony_ci	case ncsi_dev_state_probe:
14738c2ecf20Sopenharmony_ci		ncsi_probe_channel(ndp);
14748c2ecf20Sopenharmony_ci		break;
14758c2ecf20Sopenharmony_ci	case ncsi_dev_state_suspend:
14768c2ecf20Sopenharmony_ci		ncsi_suspend_channel(ndp);
14778c2ecf20Sopenharmony_ci		break;
14788c2ecf20Sopenharmony_ci	case ncsi_dev_state_config:
14798c2ecf20Sopenharmony_ci		ncsi_configure_channel(ndp);
14808c2ecf20Sopenharmony_ci		break;
14818c2ecf20Sopenharmony_ci	default:
14828c2ecf20Sopenharmony_ci		netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
14838c2ecf20Sopenharmony_ci			    nd->state);
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ciint ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
14908c2ecf20Sopenharmony_ci	int old_state;
14918c2ecf20Sopenharmony_ci	unsigned long flags;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
14948c2ecf20Sopenharmony_ci	nc = list_first_or_null_rcu(&ndp->channel_queue,
14958c2ecf20Sopenharmony_ci				    struct ncsi_channel, link);
14968c2ecf20Sopenharmony_ci	if (!nc) {
14978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
14988c2ecf20Sopenharmony_ci		goto out;
14998c2ecf20Sopenharmony_ci	}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	list_del_init(&nc->link);
15028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nc->lock, flags);
15058c2ecf20Sopenharmony_ci	old_state = nc->state;
15068c2ecf20Sopenharmony_ci	nc->state = NCSI_CHANNEL_INVISIBLE;
15078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nc->lock, flags);
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	ndp->active_channel = nc;
15108c2ecf20Sopenharmony_ci	ndp->active_package = nc->package;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	switch (old_state) {
15138c2ecf20Sopenharmony_ci	case NCSI_CHANNEL_INACTIVE:
15148c2ecf20Sopenharmony_ci		ndp->ndev.state = ncsi_dev_state_config;
15158c2ecf20Sopenharmony_ci		netdev_dbg(ndp->ndev.dev, "NCSI: configuring channel %u\n",
15168c2ecf20Sopenharmony_ci	                   nc->id);
15178c2ecf20Sopenharmony_ci		ncsi_configure_channel(ndp);
15188c2ecf20Sopenharmony_ci		break;
15198c2ecf20Sopenharmony_ci	case NCSI_CHANNEL_ACTIVE:
15208c2ecf20Sopenharmony_ci		ndp->ndev.state = ncsi_dev_state_suspend;
15218c2ecf20Sopenharmony_ci		netdev_dbg(ndp->ndev.dev, "NCSI: suspending channel %u\n",
15228c2ecf20Sopenharmony_ci			   nc->id);
15238c2ecf20Sopenharmony_ci		ncsi_suspend_channel(ndp);
15248c2ecf20Sopenharmony_ci		break;
15258c2ecf20Sopenharmony_ci	default:
15268c2ecf20Sopenharmony_ci		netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
15278c2ecf20Sopenharmony_ci			   old_state, nc->package->id, nc->id);
15288c2ecf20Sopenharmony_ci		ncsi_report_link(ndp, false);
15298c2ecf20Sopenharmony_ci		return -EINVAL;
15308c2ecf20Sopenharmony_ci	}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	return 0;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ciout:
15358c2ecf20Sopenharmony_ci	ndp->active_channel = NULL;
15368c2ecf20Sopenharmony_ci	ndp->active_package = NULL;
15378c2ecf20Sopenharmony_ci	if (ndp->flags & NCSI_DEV_RESHUFFLE) {
15388c2ecf20Sopenharmony_ci		ndp->flags &= ~NCSI_DEV_RESHUFFLE;
15398c2ecf20Sopenharmony_ci		return ncsi_choose_active_channel(ndp);
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	ncsi_report_link(ndp, false);
15438c2ecf20Sopenharmony_ci	return -ENODEV;
15448c2ecf20Sopenharmony_ci}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_cistatic int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
15478c2ecf20Sopenharmony_ci{
15488c2ecf20Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
15498c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
15508c2ecf20Sopenharmony_ci	struct ncsi_package *np;
15518c2ecf20Sopenharmony_ci	unsigned long flags;
15528c2ecf20Sopenharmony_ci	unsigned int n = 0;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
15558c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
15568c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, flags);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci			/* Channels may be busy, mark dirty instead of
15598c2ecf20Sopenharmony_ci			 * kicking if;
15608c2ecf20Sopenharmony_ci			 * a) not ACTIVE (configured)
15618c2ecf20Sopenharmony_ci			 * b) in the channel_queue (to be configured)
15628c2ecf20Sopenharmony_ci			 * c) it's ndev is in the config state
15638c2ecf20Sopenharmony_ci			 */
15648c2ecf20Sopenharmony_ci			if (nc->state != NCSI_CHANNEL_ACTIVE) {
15658c2ecf20Sopenharmony_ci				if ((ndp->ndev.state & 0xff00) ==
15668c2ecf20Sopenharmony_ci						ncsi_dev_state_config ||
15678c2ecf20Sopenharmony_ci						!list_empty(&nc->link)) {
15688c2ecf20Sopenharmony_ci					netdev_dbg(nd->dev,
15698c2ecf20Sopenharmony_ci						   "NCSI: channel %p marked dirty\n",
15708c2ecf20Sopenharmony_ci						   nc);
15718c2ecf20Sopenharmony_ci					nc->reconfigure_needed = true;
15728c2ecf20Sopenharmony_ci				}
15738c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&nc->lock, flags);
15748c2ecf20Sopenharmony_ci				continue;
15758c2ecf20Sopenharmony_ci			}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci			ncsi_stop_channel_monitor(nc);
15808c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, flags);
15818c2ecf20Sopenharmony_ci			nc->state = NCSI_CHANNEL_INACTIVE;
15828c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ndp->lock, flags);
15858c2ecf20Sopenharmony_ci			list_add_tail_rcu(&nc->link, &ndp->channel_queue);
15868c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ndp->lock, flags);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci			netdev_dbg(nd->dev, "NCSI: kicked channel %p\n", nc);
15898c2ecf20Sopenharmony_ci			n++;
15908c2ecf20Sopenharmony_ci		}
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	return n;
15948c2ecf20Sopenharmony_ci}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ciint ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
15978c2ecf20Sopenharmony_ci{
15988c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
15998c2ecf20Sopenharmony_ci	unsigned int n_vids = 0;
16008c2ecf20Sopenharmony_ci	struct vlan_vid *vlan;
16018c2ecf20Sopenharmony_ci	struct ncsi_dev *nd;
16028c2ecf20Sopenharmony_ci	bool found = false;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	if (vid == 0)
16058c2ecf20Sopenharmony_ci		return 0;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	nd = ncsi_find_dev(dev);
16088c2ecf20Sopenharmony_ci	if (!nd) {
16098c2ecf20Sopenharmony_ci		netdev_warn(dev, "NCSI: No net_device?\n");
16108c2ecf20Sopenharmony_ci		return 0;
16118c2ecf20Sopenharmony_ci	}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	ndp = TO_NCSI_DEV_PRIV(nd);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	/* Add the VLAN id to our internal list */
16168c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
16178c2ecf20Sopenharmony_ci		n_vids++;
16188c2ecf20Sopenharmony_ci		if (vlan->vid == vid) {
16198c2ecf20Sopenharmony_ci			netdev_dbg(dev, "NCSI: vid %u already registered\n",
16208c2ecf20Sopenharmony_ci				   vid);
16218c2ecf20Sopenharmony_ci			return 0;
16228c2ecf20Sopenharmony_ci		}
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci	if (n_vids >= NCSI_MAX_VLAN_VIDS) {
16258c2ecf20Sopenharmony_ci		netdev_warn(dev,
16268c2ecf20Sopenharmony_ci			    "tried to add vlan id %u but NCSI max already registered (%u)\n",
16278c2ecf20Sopenharmony_ci			    vid, NCSI_MAX_VLAN_VIDS);
16288c2ecf20Sopenharmony_ci		return -ENOSPC;
16298c2ecf20Sopenharmony_ci	}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
16328c2ecf20Sopenharmony_ci	if (!vlan)
16338c2ecf20Sopenharmony_ci		return -ENOMEM;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	vlan->proto = proto;
16368c2ecf20Sopenharmony_ci	vlan->vid = vid;
16378c2ecf20Sopenharmony_ci	list_add_rcu(&vlan->list, &ndp->vlan_vids);
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	netdev_dbg(dev, "NCSI: Added new vid %u\n", vid);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	found = ncsi_kick_channels(ndp) != 0;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	return found ? ncsi_process_next_channel(ndp) : 0;
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ciint ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
16488c2ecf20Sopenharmony_ci{
16498c2ecf20Sopenharmony_ci	struct vlan_vid *vlan, *tmp;
16508c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
16518c2ecf20Sopenharmony_ci	struct ncsi_dev *nd;
16528c2ecf20Sopenharmony_ci	bool found = false;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	if (vid == 0)
16558c2ecf20Sopenharmony_ci		return 0;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	nd = ncsi_find_dev(dev);
16588c2ecf20Sopenharmony_ci	if (!nd) {
16598c2ecf20Sopenharmony_ci		netdev_warn(dev, "NCSI: no net_device?\n");
16608c2ecf20Sopenharmony_ci		return 0;
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	ndp = TO_NCSI_DEV_PRIV(nd);
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	/* Remove the VLAN id from our internal list */
16668c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
16678c2ecf20Sopenharmony_ci		if (vlan->vid == vid) {
16688c2ecf20Sopenharmony_ci			netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
16698c2ecf20Sopenharmony_ci			list_del_rcu(&vlan->list);
16708c2ecf20Sopenharmony_ci			found = true;
16718c2ecf20Sopenharmony_ci			kfree(vlan);
16728c2ecf20Sopenharmony_ci		}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	if (!found) {
16758c2ecf20Sopenharmony_ci		netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
16768c2ecf20Sopenharmony_ci		return -EINVAL;
16778c2ecf20Sopenharmony_ci	}
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	found = ncsi_kick_channels(ndp) != 0;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	return found ? ncsi_process_next_channel(ndp) : 0;
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistruct ncsi_dev *ncsi_register_dev(struct net_device *dev,
16868c2ecf20Sopenharmony_ci				   void (*handler)(struct ncsi_dev *ndev))
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
16898c2ecf20Sopenharmony_ci	struct ncsi_dev *nd;
16908c2ecf20Sopenharmony_ci	struct platform_device *pdev;
16918c2ecf20Sopenharmony_ci	struct device_node *np;
16928c2ecf20Sopenharmony_ci	unsigned long flags;
16938c2ecf20Sopenharmony_ci	int i;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	/* Check if the device has been registered or not */
16968c2ecf20Sopenharmony_ci	nd = ncsi_find_dev(dev);
16978c2ecf20Sopenharmony_ci	if (nd)
16988c2ecf20Sopenharmony_ci		return nd;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/* Create NCSI device */
17018c2ecf20Sopenharmony_ci	ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
17028c2ecf20Sopenharmony_ci	if (!ndp)
17038c2ecf20Sopenharmony_ci		return NULL;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	nd = &ndp->ndev;
17068c2ecf20Sopenharmony_ci	nd->state = ncsi_dev_state_registered;
17078c2ecf20Sopenharmony_ci	nd->dev = dev;
17088c2ecf20Sopenharmony_ci	nd->handler = handler;
17098c2ecf20Sopenharmony_ci	ndp->pending_req_num = 0;
17108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ndp->channel_queue);
17118c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ndp->vlan_vids);
17128c2ecf20Sopenharmony_ci	INIT_WORK(&ndp->work, ncsi_dev_work);
17138c2ecf20Sopenharmony_ci	ndp->package_whitelist = UINT_MAX;
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	/* Initialize private NCSI device */
17168c2ecf20Sopenharmony_ci	spin_lock_init(&ndp->lock);
17178c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ndp->packages);
17188c2ecf20Sopenharmony_ci	ndp->request_id = NCSI_REQ_START_IDX;
17198c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
17208c2ecf20Sopenharmony_ci		ndp->requests[i].id = i;
17218c2ecf20Sopenharmony_ci		ndp->requests[i].ndp = ndp;
17228c2ecf20Sopenharmony_ci		timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0);
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ncsi_dev_lock, flags);
17268c2ecf20Sopenharmony_ci	list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
17278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ncsi_dev_lock, flags);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	/* Register NCSI packet Rx handler */
17308c2ecf20Sopenharmony_ci	ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
17318c2ecf20Sopenharmony_ci	ndp->ptype.func = ncsi_rcv_rsp;
17328c2ecf20Sopenharmony_ci	ndp->ptype.dev = dev;
17338c2ecf20Sopenharmony_ci	dev_add_pack(&ndp->ptype);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	pdev = to_platform_device(dev->dev.parent);
17368c2ecf20Sopenharmony_ci	if (pdev) {
17378c2ecf20Sopenharmony_ci		np = pdev->dev.of_node;
17388c2ecf20Sopenharmony_ci		if (np && of_get_property(np, "mlx,multi-host", NULL))
17398c2ecf20Sopenharmony_ci			ndp->mlx_multi_host = true;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	return nd;
17438c2ecf20Sopenharmony_ci}
17448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_register_dev);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ciint ncsi_start_dev(struct ncsi_dev *nd)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	if (nd->state != ncsi_dev_state_registered &&
17518c2ecf20Sopenharmony_ci	    nd->state != ncsi_dev_state_functional)
17528c2ecf20Sopenharmony_ci		return -ENOTTY;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	if (!(ndp->flags & NCSI_DEV_PROBED)) {
17558c2ecf20Sopenharmony_ci		ndp->package_probe_id = 0;
17568c2ecf20Sopenharmony_ci		nd->state = ncsi_dev_state_probe;
17578c2ecf20Sopenharmony_ci		schedule_work(&ndp->work);
17588c2ecf20Sopenharmony_ci		return 0;
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return ncsi_reset_dev(nd);
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_start_dev);
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_civoid ncsi_stop_dev(struct ncsi_dev *nd)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
17688c2ecf20Sopenharmony_ci	struct ncsi_package *np;
17698c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
17708c2ecf20Sopenharmony_ci	bool chained;
17718c2ecf20Sopenharmony_ci	int old_state;
17728c2ecf20Sopenharmony_ci	unsigned long flags;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	/* Stop the channel monitor on any active channels. Don't reset the
17758c2ecf20Sopenharmony_ci	 * channel state so we know which were active when ncsi_start_dev()
17768c2ecf20Sopenharmony_ci	 * is next called.
17778c2ecf20Sopenharmony_ci	 */
17788c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
17798c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
17808c2ecf20Sopenharmony_ci			ncsi_stop_channel_monitor(nc);
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, flags);
17838c2ecf20Sopenharmony_ci			chained = !list_empty(&nc->link);
17848c2ecf20Sopenharmony_ci			old_state = nc->state;
17858c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci			WARN_ON_ONCE(chained ||
17888c2ecf20Sopenharmony_ci				     old_state == NCSI_CHANNEL_INVISIBLE);
17898c2ecf20Sopenharmony_ci		}
17908c2ecf20Sopenharmony_ci	}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	netdev_dbg(ndp->ndev.dev, "NCSI: Stopping device\n");
17938c2ecf20Sopenharmony_ci	ncsi_report_link(ndp, true);
17948c2ecf20Sopenharmony_ci}
17958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_stop_dev);
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ciint ncsi_reset_dev(struct ncsi_dev *nd)
17988c2ecf20Sopenharmony_ci{
17998c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
18008c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *active, *tmp;
18018c2ecf20Sopenharmony_ci	struct ncsi_package *np;
18028c2ecf20Sopenharmony_ci	unsigned long flags;
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	if (!(ndp->flags & NCSI_DEV_RESET)) {
18078c2ecf20Sopenharmony_ci		/* Haven't been called yet, check states */
18088c2ecf20Sopenharmony_ci		switch (nd->state & ncsi_dev_state_major) {
18098c2ecf20Sopenharmony_ci		case ncsi_dev_state_registered:
18108c2ecf20Sopenharmony_ci		case ncsi_dev_state_probe:
18118c2ecf20Sopenharmony_ci			/* Not even probed yet - do nothing */
18128c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ndp->lock, flags);
18138c2ecf20Sopenharmony_ci			return 0;
18148c2ecf20Sopenharmony_ci		case ncsi_dev_state_suspend:
18158c2ecf20Sopenharmony_ci		case ncsi_dev_state_config:
18168c2ecf20Sopenharmony_ci			/* Wait for the channel to finish its suspend/config
18178c2ecf20Sopenharmony_ci			 * operation; once it finishes it will check for
18188c2ecf20Sopenharmony_ci			 * NCSI_DEV_RESET and reset the state.
18198c2ecf20Sopenharmony_ci			 */
18208c2ecf20Sopenharmony_ci			ndp->flags |= NCSI_DEV_RESET;
18218c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ndp->lock, flags);
18228c2ecf20Sopenharmony_ci			return 0;
18238c2ecf20Sopenharmony_ci		}
18248c2ecf20Sopenharmony_ci	} else {
18258c2ecf20Sopenharmony_ci		switch (nd->state) {
18268c2ecf20Sopenharmony_ci		case ncsi_dev_state_suspend_done:
18278c2ecf20Sopenharmony_ci		case ncsi_dev_state_config_done:
18288c2ecf20Sopenharmony_ci		case ncsi_dev_state_functional:
18298c2ecf20Sopenharmony_ci			/* Ok */
18308c2ecf20Sopenharmony_ci			break;
18318c2ecf20Sopenharmony_ci		default:
18328c2ecf20Sopenharmony_ci			/* Current reset operation happening */
18338c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ndp->lock, flags);
18348c2ecf20Sopenharmony_ci			return 0;
18358c2ecf20Sopenharmony_ci		}
18368c2ecf20Sopenharmony_ci	}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	if (!list_empty(&ndp->channel_queue)) {
18398c2ecf20Sopenharmony_ci		/* Clear any channel queue we may have interrupted */
18408c2ecf20Sopenharmony_ci		list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
18418c2ecf20Sopenharmony_ci			list_del_init(&nc->link);
18428c2ecf20Sopenharmony_ci	}
18438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	active = NULL;
18468c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
18478c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
18488c2ecf20Sopenharmony_ci			spin_lock_irqsave(&nc->lock, flags);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci			if (nc->state == NCSI_CHANNEL_ACTIVE) {
18518c2ecf20Sopenharmony_ci				active = nc;
18528c2ecf20Sopenharmony_ci				nc->state = NCSI_CHANNEL_INVISIBLE;
18538c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&nc->lock, flags);
18548c2ecf20Sopenharmony_ci				ncsi_stop_channel_monitor(nc);
18558c2ecf20Sopenharmony_ci				break;
18568c2ecf20Sopenharmony_ci			}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&nc->lock, flags);
18598c2ecf20Sopenharmony_ci		}
18608c2ecf20Sopenharmony_ci		if (active)
18618c2ecf20Sopenharmony_ci			break;
18628c2ecf20Sopenharmony_ci	}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (!active) {
18658c2ecf20Sopenharmony_ci		/* Done */
18668c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ndp->lock, flags);
18678c2ecf20Sopenharmony_ci		ndp->flags &= ~NCSI_DEV_RESET;
18688c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ndp->lock, flags);
18698c2ecf20Sopenharmony_ci		return ncsi_choose_active_channel(ndp);
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
18738c2ecf20Sopenharmony_ci	ndp->flags |= NCSI_DEV_RESET;
18748c2ecf20Sopenharmony_ci	ndp->active_channel = active;
18758c2ecf20Sopenharmony_ci	ndp->active_package = active->package;
18768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	nd->state = ncsi_dev_state_suspend;
18798c2ecf20Sopenharmony_ci	schedule_work(&ndp->work);
18808c2ecf20Sopenharmony_ci	return 0;
18818c2ecf20Sopenharmony_ci}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_civoid ncsi_unregister_dev(struct ncsi_dev *nd)
18848c2ecf20Sopenharmony_ci{
18858c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
18868c2ecf20Sopenharmony_ci	struct ncsi_package *np, *tmp;
18878c2ecf20Sopenharmony_ci	unsigned long flags;
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	dev_remove_pack(&ndp->ptype);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	list_for_each_entry_safe(np, tmp, &ndp->packages, node)
18928c2ecf20Sopenharmony_ci		ncsi_remove_package(np);
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ncsi_dev_lock, flags);
18958c2ecf20Sopenharmony_ci	list_del_rcu(&ndp->node);
18968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ncsi_dev_lock, flags);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	kfree(ndp);
18998c2ecf20Sopenharmony_ci}
19008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ncsi_unregister_dev);
1901