18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 68c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 128c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 138c2ecf20Sopenharmony_ci * conditions are met: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 168c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 178c2ecf20Sopenharmony_ci * disclaimer. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 208c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 218c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 228c2ecf20Sopenharmony_ci * provided with the distribution. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 318c2ecf20Sopenharmony_ci * SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <linux/export.h> 368c2ecf20Sopenharmony_ci#include <linux/errno.h> 378c2ecf20Sopenharmony_ci#include <net/devlink.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "mlx4.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct mlx4_device_context { 428c2ecf20Sopenharmony_ci struct list_head list; 438c2ecf20Sopenharmony_ci struct list_head bond_list; 448c2ecf20Sopenharmony_ci struct mlx4_interface *intf; 458c2ecf20Sopenharmony_ci void *context; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic LIST_HEAD(intf_list); 498c2ecf20Sopenharmony_cistatic LIST_HEAD(dev_list); 508c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(intf_mutex); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void mlx4_add_device(struct mlx4_interface *intf, struct mlx4_priv *priv) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mlx4_device_context *dev_ctx; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); 578c2ecf20Sopenharmony_ci if (!dev_ctx) 588c2ecf20Sopenharmony_ci return; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci dev_ctx->intf = intf; 618c2ecf20Sopenharmony_ci dev_ctx->context = intf->add(&priv->dev); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (dev_ctx->context) { 648c2ecf20Sopenharmony_ci spin_lock_irq(&priv->ctx_lock); 658c2ecf20Sopenharmony_ci list_add_tail(&dev_ctx->list, &priv->ctx_list); 668c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->ctx_lock); 678c2ecf20Sopenharmony_ci if (intf->activate) 688c2ecf20Sopenharmony_ci intf->activate(&priv->dev, dev_ctx->context); 698c2ecf20Sopenharmony_ci } else 708c2ecf20Sopenharmony_ci kfree(dev_ctx); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void mlx4_remove_device(struct mlx4_interface *intf, struct mlx4_priv *priv) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct mlx4_device_context *dev_ctx; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci list_for_each_entry(dev_ctx, &priv->ctx_list, list) 798c2ecf20Sopenharmony_ci if (dev_ctx->intf == intf) { 808c2ecf20Sopenharmony_ci spin_lock_irq(&priv->ctx_lock); 818c2ecf20Sopenharmony_ci list_del(&dev_ctx->list); 828c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->ctx_lock); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci intf->remove(&priv->dev, dev_ctx->context); 858c2ecf20Sopenharmony_ci kfree(dev_ctx); 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ciint mlx4_register_interface(struct mlx4_interface *intf) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct mlx4_priv *priv; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!intf->add || !intf->remove) 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci mutex_lock(&intf_mutex); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci list_add_tail(&intf->list, &intf_list); 1008c2ecf20Sopenharmony_ci list_for_each_entry(priv, &dev_list, dev_list) { 1018c2ecf20Sopenharmony_ci if (mlx4_is_mfunc(&priv->dev) && (intf->flags & MLX4_INTFF_BONDING)) { 1028c2ecf20Sopenharmony_ci mlx4_dbg(&priv->dev, 1038c2ecf20Sopenharmony_ci "SRIOV, disabling HA mode for intf proto %d\n", intf->protocol); 1048c2ecf20Sopenharmony_ci intf->flags &= ~MLX4_INTFF_BONDING; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci mlx4_add_device(intf, priv); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci mutex_unlock(&intf_mutex); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_register_interface); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_civoid mlx4_unregister_interface(struct mlx4_interface *intf) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct mlx4_priv *priv; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci mutex_lock(&intf_mutex); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci list_for_each_entry(priv, &dev_list, dev_list) 1228c2ecf20Sopenharmony_ci mlx4_remove_device(intf, priv); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci list_del(&intf->list); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mutex_unlock(&intf_mutex); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_unregister_interface); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciint mlx4_do_bond(struct mlx4_dev *dev, bool enable) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 1338c2ecf20Sopenharmony_ci struct mlx4_device_context *dev_ctx = NULL, *temp_dev_ctx; 1348c2ecf20Sopenharmony_ci unsigned long flags; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci LIST_HEAD(bond_list); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) 1398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = mlx4_disable_rx_port_check(dev, enable); 1428c2ecf20Sopenharmony_ci if (ret) { 1438c2ecf20Sopenharmony_ci mlx4_err(dev, "Fail to %s rx port check\n", 1448c2ecf20Sopenharmony_ci enable ? "enable" : "disable"); 1458c2ecf20Sopenharmony_ci return ret; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (enable) { 1488c2ecf20Sopenharmony_ci dev->flags |= MLX4_FLAG_BONDED; 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci ret = mlx4_virt2phy_port_map(dev, 1, 2); 1518c2ecf20Sopenharmony_ci if (ret) { 1528c2ecf20Sopenharmony_ci mlx4_err(dev, "Fail to reset port map\n"); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci dev->flags &= ~MLX4_FLAG_BONDED; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ctx_lock, flags); 1598c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev_ctx, temp_dev_ctx, &priv->ctx_list, list) { 1608c2ecf20Sopenharmony_ci if (dev_ctx->intf->flags & MLX4_INTFF_BONDING) { 1618c2ecf20Sopenharmony_ci list_add_tail(&dev_ctx->bond_list, &bond_list); 1628c2ecf20Sopenharmony_ci list_del(&dev_ctx->list); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ctx_lock, flags); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci list_for_each_entry(dev_ctx, &bond_list, bond_list) { 1688c2ecf20Sopenharmony_ci dev_ctx->intf->remove(dev, dev_ctx->context); 1698c2ecf20Sopenharmony_ci dev_ctx->context = dev_ctx->intf->add(dev); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ctx_lock, flags); 1728c2ecf20Sopenharmony_ci list_add_tail(&dev_ctx->list, &priv->ctx_list); 1738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ctx_lock, flags); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mlx4_dbg(dev, "Interface for protocol %d restarted with bonded mode %s\n", 1768c2ecf20Sopenharmony_ci dev_ctx->intf->protocol, enable ? 1778c2ecf20Sopenharmony_ci "enabled" : "disabled"); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_civoid mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, 1838c2ecf20Sopenharmony_ci unsigned long param) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 1868c2ecf20Sopenharmony_ci struct mlx4_device_context *dev_ctx; 1878c2ecf20Sopenharmony_ci unsigned long flags; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ctx_lock, flags); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci list_for_each_entry(dev_ctx, &priv->ctx_list, list) 1928c2ecf20Sopenharmony_ci if (dev_ctx->intf->event) 1938c2ecf20Sopenharmony_ci dev_ctx->intf->event(dev, dev_ctx->context, type, param); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ctx_lock, flags); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint mlx4_register_device(struct mlx4_dev *dev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 2018c2ecf20Sopenharmony_ci struct mlx4_interface *intf; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci mutex_lock(&intf_mutex); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dev->persist->interface_state |= MLX4_INTERFACE_STATE_UP; 2068c2ecf20Sopenharmony_ci list_add_tail(&priv->dev_list, &dev_list); 2078c2ecf20Sopenharmony_ci list_for_each_entry(intf, &intf_list, list) 2088c2ecf20Sopenharmony_ci mlx4_add_device(intf, priv); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci mutex_unlock(&intf_mutex); 2118c2ecf20Sopenharmony_ci mlx4_start_catas_poll(dev); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid mlx4_unregister_device(struct mlx4_dev *dev) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 2198c2ecf20Sopenharmony_ci struct mlx4_interface *intf; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!(dev->persist->interface_state & MLX4_INTERFACE_STATE_UP)) 2228c2ecf20Sopenharmony_ci return; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mlx4_stop_catas_poll(dev); 2258c2ecf20Sopenharmony_ci if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION && 2268c2ecf20Sopenharmony_ci mlx4_is_slave(dev)) { 2278c2ecf20Sopenharmony_ci /* In mlx4_remove_one on a VF */ 2288c2ecf20Sopenharmony_ci u32 slave_read = 2298c2ecf20Sopenharmony_ci swab32(readl(&mlx4_priv(dev)->mfunc.comm->slave_read)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (mlx4_comm_internal_err(slave_read)) { 2328c2ecf20Sopenharmony_ci mlx4_dbg(dev, "%s: comm channel is down, entering error state.\n", 2338c2ecf20Sopenharmony_ci __func__); 2348c2ecf20Sopenharmony_ci mlx4_enter_error_state(dev->persist); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci mutex_lock(&intf_mutex); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci list_for_each_entry(intf, &intf_list, list) 2408c2ecf20Sopenharmony_ci mlx4_remove_device(intf, priv); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci list_del(&priv->dev_list); 2438c2ecf20Sopenharmony_ci dev->persist->interface_state &= ~MLX4_INTERFACE_STATE_UP; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mutex_unlock(&intf_mutex); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_civoid *mlx4_get_protocol_dev(struct mlx4_dev *dev, enum mlx4_protocol proto, int port) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct mlx4_priv *priv = mlx4_priv(dev); 2518c2ecf20Sopenharmony_ci struct mlx4_device_context *dev_ctx; 2528c2ecf20Sopenharmony_ci unsigned long flags; 2538c2ecf20Sopenharmony_ci void *result = NULL; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->ctx_lock, flags); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci list_for_each_entry(dev_ctx, &priv->ctx_list, list) 2588c2ecf20Sopenharmony_ci if (dev_ctx->intf->protocol == proto && dev_ctx->intf->get_dev) { 2598c2ecf20Sopenharmony_ci result = dev_ctx->intf->get_dev(dev, dev_ctx->context, port); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->ctx_lock, flags); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return result; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_get_protocol_dev); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistruct devlink_port *mlx4_get_devlink_port(struct mlx4_dev *dev, int port) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return &info->devlink_port; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_get_devlink_port); 276