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