| /* | 
 |  * Copyright (c) 2007 Mellanox Technologies. All rights reserved. | 
 |  * | 
 |  * This software is available to you under a choice of one of two | 
 |  * licenses.  You may choose to be licensed under the terms of the GNU | 
 |  * General Public License (GPL) Version 2, available from the file | 
 |  * COPYING in the main directory of this source tree, or the | 
 |  * OpenIB.org BSD license below: | 
 |  * | 
 |  *     Redistribution and use in source and binary forms, with or | 
 |  *     without modification, are permitted provided that the following | 
 |  *     conditions are met: | 
 |  * | 
 |  *      - Redistributions of source code must retain the above | 
 |  *        copyright notice, this list of conditions and the following | 
 |  *        disclaimer. | 
 |  * | 
 |  *      - Redistributions in binary form must reproduce the above | 
 |  *        copyright notice, this list of conditions and the following | 
 |  *        disclaimer in the documentation and/or other materials | 
 |  *        provided with the distribution. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
 |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
 |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
 |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
 |  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
 |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
 |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. | 
 |  */ | 
 |  | 
 | #include <linux_compat.h> | 
 | #include <linux/mlx4/cmd.h> | 
 |  | 
 | #include "mlx4.h" | 
 | #include "mlx4_stats.h" | 
 |  | 
 | #define MLX4_MAC_VALID		(1ull << 63) | 
 |  | 
 | #define MLX4_VLAN_VALID		(1u << 31) | 
 | #define MLX4_VLAN_MASK		0xfff | 
 |  | 
 | #define MLX4_STATS_TRAFFIC_COUNTERS_MASK	0xfULL | 
 | #define MLX4_STATS_TRAFFIC_DROPS_MASK		0xc0ULL | 
 | #define MLX4_STATS_ERROR_COUNTERS_MASK		0x1ffc30ULL | 
 | #define MLX4_STATS_PORT_COUNTERS_MASK		0x1fe00000ULL | 
 |  | 
 | #define MLX4_FLAG_V_IGNORE_FCS_MASK		0x2 | 
 | #define MLX4_IGNORE_FCS_MASK			0x1 | 
 |  | 
 | void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	qlock_init(&table->mutex); | 
 | 	for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { | 
 | 		table->entries[i] = 0; | 
 | 		table->refs[i]	 = 0; | 
 | 	} | 
 | 	table->max   = 1 << dev->caps.log_num_macs; | 
 | 	table->total = 0; | 
 | } | 
 |  | 
 | void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	qlock_init(&table->mutex); | 
 | 	for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { | 
 | 		table->entries[i] = 0; | 
 | 		table->refs[i]	 = 0; | 
 | 	} | 
 | 	table->max   = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR; | 
 | 	table->total = 0; | 
 | } | 
 |  | 
 | void mlx4_init_roce_gid_table(struct mlx4_dev *dev, | 
 | 			      struct mlx4_roce_gid_table *table) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	qlock_init(&table->mutex); | 
 | 	for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) | 
 | 		memset(table->roce_gids[i].raw, 0, MLX4_ROCE_GID_ENTRY_SIZE); | 
 | } | 
 |  | 
 | static int validate_index(struct mlx4_dev *dev, | 
 | 			  struct mlx4_mac_table *table, int index) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (index < 0 || index >= table->max || !table->entries[index]) { | 
 | 		mlx4_warn(dev, "No valid Mac entry for the given index\n"); | 
 | 		err = -EINVAL; | 
 | 	} | 
 | 	return err; | 
 | } | 
 |  | 
 | static int find_index(struct mlx4_dev *dev, | 
 | 		      struct mlx4_mac_table *table, uint64_t mac) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { | 
 | 		if (table->refs[i] && | 
 | 		    (MLX4_MAC_MASK & mac) == | 
 | 		    (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))) | 
 | 			return i; | 
 | 	} | 
 | 	/* Mac not found */ | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static int mlx4_set_port_mac_table(struct mlx4_dev *dev, uint8_t port, | 
 | 				   __be64 *entries) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	uint32_t in_mod; | 
 | 	int err; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 |  | 
 | 	memcpy(mailbox->buf, entries, MLX4_MAC_TABLE_SIZE); | 
 |  | 
 | 	in_mod = MLX4_SET_PORT_MAC_TABLE << 8 | port; | 
 |  | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_NATIVE); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_find_cached_mac(struct mlx4_dev *dev, uint8_t port, uint64_t mac, | 
 | 			 int *idx) | 
 | { | 
 | 	struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; | 
 | 	struct mlx4_mac_table *table = &info->mac_table; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { | 
 | 		if (!table->refs[i]) | 
 | 			continue; | 
 |  | 
 | 		if (mac == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))) { | 
 | 			*idx = i; | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return -ENOENT; | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_find_cached_mac); | 
 |  | 
 | int __mlx4_register_mac(struct mlx4_dev *dev, uint8_t port, uint64_t mac) | 
 | { | 
 | 	struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; | 
 | 	struct mlx4_mac_table *table = &info->mac_table; | 
 | 	int i, err = 0; | 
 | 	int free = -1; | 
 |  | 
 | 	mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d\n", | 
 | 		 (unsigned long long) mac, port); | 
 |  | 
 | 	qlock(&table->mutex); | 
 | 	for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { | 
 | 		if (!table->refs[i]) { | 
 | 			if (free < 0) | 
 | 				free = i; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if ((MLX4_MAC_MASK & mac) == | 
 | 		     (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))) { | 
 | 			/* MAC already registered, increment ref count */ | 
 | 			err = i; | 
 | 			++table->refs[i]; | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mlx4_dbg(dev, "Free MAC index is %d\n", free); | 
 |  | 
 | 	if (table->total == table->max) { | 
 | 		/* No free mac entries */ | 
 | 		err = -ENOSPC; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* Register new MAC */ | 
 | 	table->entries[free] = cpu_to_be64(mac | MLX4_MAC_VALID); | 
 |  | 
 | 	err = mlx4_set_port_mac_table(dev, port, table->entries); | 
 | 	if (unlikely(err)) { | 
 | 		mlx4_err(dev, "Failed adding MAC: 0x%llx\n", | 
 | 			 (unsigned long long) mac); | 
 | 		table->entries[free] = 0; | 
 | 		goto out; | 
 | 	} | 
 | 	table->refs[free] = 1; | 
 | 	err = free; | 
 | 	++table->total; | 
 | out: | 
 | 	qunlock(&table->mutex); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(__mlx4_register_mac); | 
 |  | 
 | int mlx4_register_mac(struct mlx4_dev *dev, uint8_t port, uint64_t mac) | 
 | { | 
 | 	uint64_t out_param = 0; | 
 | 	int err = -EINVAL; | 
 |  | 
 | 	if (mlx4_is_mfunc(dev)) { | 
 | 		if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) { | 
 | 			err = mlx4_cmd_imm(dev, mac, &out_param, | 
 | 					   ((uint32_t) port) << 8 | (uint32_t) RES_MAC, | 
 | 					   RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, | 
 | 					   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); | 
 | 		} | 
 | 		if (err && err == -EINVAL && mlx4_is_slave(dev)) { | 
 | 			/* retry using old REG_MAC format */ | 
 | 			set_param_l(&out_param, port); | 
 | 			err = mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, | 
 | 					   RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, | 
 | 					   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); | 
 | 			if (!err) | 
 | 				dev->flags |= MLX4_FLAG_OLD_REG_MAC; | 
 | 		} | 
 | 		if (err) | 
 | 			return err; | 
 |  | 
 | 		return get_param_l(&out_param); | 
 | 	} | 
 | 	return __mlx4_register_mac(dev, port, mac); | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_register_mac); | 
 |  | 
 | int mlx4_get_base_qpn(struct mlx4_dev *dev, uint8_t port) | 
 | { | 
 | 	return dev->caps.reserved_qps_base[MLX4_QP_REGION_ETH_ADDR] + | 
 | 			(port - 1) * (1 << dev->caps.log_num_macs); | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_get_base_qpn); | 
 |  | 
 | void __mlx4_unregister_mac(struct mlx4_dev *dev, uint8_t port, uint64_t mac) | 
 | { | 
 | 	struct mlx4_port_info *info; | 
 | 	struct mlx4_mac_table *table; | 
 | 	int index; | 
 |  | 
 | 	if (port < 1 || port > dev->caps.num_ports) { | 
 | 		mlx4_warn(dev, "invalid port number (%d), aborting...\n", port); | 
 | 		return; | 
 | 	} | 
 | 	info = &mlx4_priv(dev)->port[port]; | 
 | 	table = &info->mac_table; | 
 | 	qlock(&table->mutex); | 
 | 	index = find_index(dev, table, mac); | 
 |  | 
 | 	if (validate_index(dev, table, index)) | 
 | 		goto out; | 
 | 	if (--table->refs[index]) { | 
 | 		mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n", | 
 | 			 index); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	table->entries[index] = 0; | 
 | 	mlx4_set_port_mac_table(dev, port, table->entries); | 
 | 	--table->total; | 
 | out: | 
 | 	qunlock(&table->mutex); | 
 | } | 
 | EXPORT_SYMBOL_GPL(__mlx4_unregister_mac); | 
 |  | 
 | void mlx4_unregister_mac(struct mlx4_dev *dev, uint8_t port, uint64_t mac) | 
 | { | 
 | 	uint64_t out_param = 0; | 
 |  | 
 | 	if (mlx4_is_mfunc(dev)) { | 
 | 		if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) { | 
 | 			(void) mlx4_cmd_imm(dev, mac, &out_param, | 
 | 					    ((uint32_t) port) << 8 | (uint32_t) RES_MAC, | 
 | 					    RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES, | 
 | 					    MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); | 
 | 		} else { | 
 | 			/* use old unregister mac format */ | 
 | 			set_param_l(&out_param, port); | 
 | 			(void) mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, | 
 | 					    RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES, | 
 | 					    MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); | 
 | 		} | 
 | 		return; | 
 | 	} | 
 | 	__mlx4_unregister_mac(dev, port, mac); | 
 | 	return; | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_unregister_mac); | 
 |  | 
 | int __mlx4_replace_mac(struct mlx4_dev *dev, uint8_t port, int qpn, | 
 | 		       uint64_t new_mac) | 
 | { | 
 | 	struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; | 
 | 	struct mlx4_mac_table *table = &info->mac_table; | 
 | 	int index = qpn - info->base_qpn; | 
 | 	int err = 0; | 
 |  | 
 | 	/* CX1 doesn't support multi-functions */ | 
 | 	qlock(&table->mutex); | 
 |  | 
 | 	err = validate_index(dev, table, index); | 
 | 	if (err) | 
 | 		goto out; | 
 |  | 
 | 	table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID); | 
 |  | 
 | 	err = mlx4_set_port_mac_table(dev, port, table->entries); | 
 | 	if (unlikely(err)) { | 
 | 		mlx4_err(dev, "Failed adding MAC: 0x%llx\n", | 
 | 			 (unsigned long long) new_mac); | 
 | 		table->entries[index] = 0; | 
 | 	} | 
 | out: | 
 | 	qunlock(&table->mutex); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(__mlx4_replace_mac); | 
 |  | 
 | static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, uint8_t port, | 
 | 				    __be32 *entries) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	uint32_t in_mod; | 
 | 	int err; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 |  | 
 | 	memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE); | 
 | 	in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port; | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_NATIVE); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_find_cached_vlan(struct mlx4_dev *dev, uint8_t port, uint16_t vid, | 
 | 			  int *idx) | 
 | { | 
 | 	struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < MLX4_MAX_VLAN_NUM; ++i) { | 
 | 		if (table->refs[i] && | 
 | 		    (vid == (MLX4_VLAN_MASK & | 
 | 			      be32_to_cpu(table->entries[i])))) { | 
 | 			/* VLAN already registered, increase reference count */ | 
 | 			*idx = i; | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return -ENOENT; | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_find_cached_vlan); | 
 |  | 
 | int __mlx4_register_vlan(struct mlx4_dev *dev, uint8_t port, uint16_t vlan, | 
 | 				int *index) | 
 | { | 
 | 	struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; | 
 | 	int i, err = 0; | 
 | 	int free = -1; | 
 |  | 
 | 	qlock(&table->mutex); | 
 |  | 
 | 	if (table->total == table->max) { | 
 | 		/* No free vlan entries */ | 
 | 		err = -ENOSPC; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) { | 
 | 		if (free < 0 && (table->refs[i] == 0)) { | 
 | 			free = i; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (table->refs[i] && | 
 | 		    (vlan == (MLX4_VLAN_MASK & | 
 | 			      be32_to_cpu(table->entries[i])))) { | 
 | 			/* Vlan already registered, increase references count */ | 
 | 			*index = i; | 
 | 			++table->refs[i]; | 
 | 			goto out; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (free < 0) { | 
 | 		err = -ENOMEM; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* Register new VLAN */ | 
 | 	table->refs[free] = 1; | 
 | 	table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID); | 
 |  | 
 | 	err = mlx4_set_port_vlan_table(dev, port, table->entries); | 
 | 	if (unlikely(err)) { | 
 | 		mlx4_warn(dev, "Failed adding vlan: %u\n", vlan); | 
 | 		table->refs[free] = 0; | 
 | 		table->entries[free] = 0; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	*index = free; | 
 | 	++table->total; | 
 | out: | 
 | 	qunlock(&table->mutex); | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_register_vlan(struct mlx4_dev *dev, uint8_t port, uint16_t vlan, | 
 | 		       int *index) | 
 | { | 
 | 	uint64_t out_param = 0; | 
 | 	int err; | 
 |  | 
 | 	if (vlan > 4095) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (mlx4_is_mfunc(dev)) { | 
 | 		err = mlx4_cmd_imm(dev, vlan, &out_param, | 
 | 				   ((uint32_t) port) << 8 | (uint32_t) RES_VLAN, | 
 | 				   RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, | 
 | 				   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); | 
 | 		if (!err) | 
 | 			*index = get_param_l(&out_param); | 
 |  | 
 | 		return err; | 
 | 	} | 
 | 	return __mlx4_register_vlan(dev, port, vlan, index); | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_register_vlan); | 
 |  | 
 | void __mlx4_unregister_vlan(struct mlx4_dev *dev, uint8_t port, uint16_t vlan) | 
 | { | 
 | 	struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; | 
 | 	int index; | 
 |  | 
 | 	qlock(&table->mutex); | 
 | 	if (mlx4_find_cached_vlan(dev, port, vlan, &index)) { | 
 | 		mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (index < MLX4_VLAN_REGULAR) { | 
 | 		mlx4_warn(dev, "Trying to free special vlan index %d\n", index); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (--table->refs[index]) { | 
 | 		mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n", | 
 | 			 table->refs[index], index); | 
 | 		goto out; | 
 | 	} | 
 | 	table->entries[index] = 0; | 
 | 	mlx4_set_port_vlan_table(dev, port, table->entries); | 
 | 	--table->total; | 
 | out: | 
 | 	qunlock(&table->mutex); | 
 | } | 
 |  | 
 | void mlx4_unregister_vlan(struct mlx4_dev *dev, uint8_t port, uint16_t vlan) | 
 | { | 
 | 	uint64_t out_param = 0; | 
 |  | 
 | 	if (mlx4_is_mfunc(dev)) { | 
 | 		(void) mlx4_cmd_imm(dev, vlan, &out_param, | 
 | 				    ((uint32_t) port) << 8 | (uint32_t) RES_VLAN, | 
 | 				    RES_OP_RESERVE_AND_MAP, | 
 | 				    MLX4_CMD_FREE_RES, MLX4_CMD_TIME_CLASS_A, | 
 | 				    MLX4_CMD_WRAPPED); | 
 | 		return; | 
 | 	} | 
 | 	__mlx4_unregister_vlan(dev, port, vlan); | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_unregister_vlan); | 
 |  | 
 | int mlx4_get_port_ib_caps(struct mlx4_dev *dev, uint8_t port, __be32 *caps) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *inmailbox, *outmailbox; | 
 | 	uint8_t *inbuf, *outbuf; | 
 | 	int err; | 
 |  | 
 | 	inmailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(inmailbox)) | 
 | 		return PTR_ERR(inmailbox); | 
 |  | 
 | 	outmailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(outmailbox)) { | 
 | 		mlx4_free_cmd_mailbox(dev, inmailbox); | 
 | 		return PTR_ERR(outmailbox); | 
 | 	} | 
 |  | 
 | 	inbuf = inmailbox->buf; | 
 | 	outbuf = outmailbox->buf; | 
 | 	inbuf[0] = 1; | 
 | 	inbuf[1] = 1; | 
 | 	inbuf[2] = 1; | 
 | 	inbuf[3] = 1; | 
 | 	*(__be16 *) (&inbuf[16]) = cpu_to_be16(0x0015); | 
 | 	*(__be32 *) (&inbuf[20]) = cpu_to_be32(port); | 
 |  | 
 | 	err = mlx4_cmd_box(dev, inmailbox->dma, outmailbox->dma, port, 3, | 
 | 			   MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C, | 
 | 			   MLX4_CMD_NATIVE); | 
 | 	if (!err) | 
 | 		*caps = *(__be32 *) (outbuf + 84); | 
 | 	mlx4_free_cmd_mailbox(dev, inmailbox); | 
 | 	mlx4_free_cmd_mailbox(dev, outmailbox); | 
 | 	return err; | 
 | } | 
 | static struct mlx4_roce_gid_entry zgid_entry; | 
 |  | 
 | int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port) | 
 | { | 
 | 	int vfs; | 
 | 	int slave_gid = slave; | 
 | 	unsigned i; | 
 | 	struct mlx4_slaves_pport slaves_pport; | 
 | 	struct mlx4_active_ports actv_ports; | 
 | 	unsigned max_port_p_one; | 
 |  | 
 | 	if (slave == 0) | 
 | 		return MLX4_ROCE_PF_GIDS; | 
 |  | 
 | 	/* Slave is a VF */ | 
 | 	slaves_pport = mlx4_phys_to_slaves_pport(dev, port); | 
 | 	actv_ports = mlx4_get_active_ports(dev, slave); | 
 | 	max_port_p_one = find_first_bit(actv_ports.ports, dev->caps.num_ports) + | 
 | 		bitmap_weight(actv_ports.ports, dev->caps.num_ports) + 1; | 
 |  | 
 | 	for (i = 1; i < max_port_p_one; i++) { | 
 | 		struct mlx4_active_ports exclusive_ports; | 
 | 		struct mlx4_slaves_pport slaves_pport_actv; | 
 | 		bitmap_zero(exclusive_ports.ports, dev->caps.num_ports); | 
 | 		set_bit(i - 1, exclusive_ports.ports); | 
 | 		if (i == port) | 
 | 			continue; | 
 | 		slaves_pport_actv = mlx4_phys_to_slaves_pport_actv( | 
 | 				    dev, &exclusive_ports); | 
 | 		slave_gid -= bitmap_weight(slaves_pport_actv.slaves, | 
 | 					   dev->persist->num_vfs + 1); | 
 | 	} | 
 | 	vfs = bitmap_weight(slaves_pport.slaves, dev->persist->num_vfs + 1) - 1; | 
 | 	if (slave_gid <= ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) % vfs)) | 
 | 		return ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs) + 1; | 
 | 	return (MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs; | 
 | } | 
 |  | 
 | int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave, int port) | 
 | { | 
 | 	int gids; | 
 | 	unsigned i; | 
 | 	int slave_gid = slave; | 
 | 	int vfs; | 
 |  | 
 | 	struct mlx4_slaves_pport slaves_pport; | 
 | 	struct mlx4_active_ports actv_ports; | 
 | 	unsigned max_port_p_one; | 
 |  | 
 | 	if (slave == 0) | 
 | 		return 0; | 
 |  | 
 | 	slaves_pport = mlx4_phys_to_slaves_pport(dev, port); | 
 | 	actv_ports = mlx4_get_active_ports(dev, slave); | 
 | 	max_port_p_one = find_first_bit(actv_ports.ports, dev->caps.num_ports) + | 
 | 		bitmap_weight(actv_ports.ports, dev->caps.num_ports) + 1; | 
 |  | 
 | 	for (i = 1; i < max_port_p_one; i++) { | 
 | 		struct mlx4_active_ports exclusive_ports; | 
 | 		struct mlx4_slaves_pport slaves_pport_actv; | 
 | 		bitmap_zero(exclusive_ports.ports, dev->caps.num_ports); | 
 | 		set_bit(i - 1, exclusive_ports.ports); | 
 | 		if (i == port) | 
 | 			continue; | 
 | 		slaves_pport_actv = mlx4_phys_to_slaves_pport_actv( | 
 | 				    dev, &exclusive_ports); | 
 | 		slave_gid -= bitmap_weight(slaves_pport_actv.slaves, | 
 | 					   dev->persist->num_vfs + 1); | 
 | 	} | 
 | 	gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS; | 
 | 	vfs = bitmap_weight(slaves_pport.slaves, dev->persist->num_vfs + 1) - 1; | 
 | 	if (slave_gid <= gids % vfs) | 
 | 		return MLX4_ROCE_PF_GIDS + ((gids / vfs) + 1) * (slave_gid - 1); | 
 |  | 
 | 	return MLX4_ROCE_PF_GIDS + (gids % vfs) + | 
 | 		((gids / vfs) * (slave_gid - 1)); | 
 | } | 
 | EXPORT_SYMBOL_GPL(mlx4_get_base_gid_ix); | 
 |  | 
 | static int mlx4_reset_roce_port_gids(struct mlx4_dev *dev, int slave, | 
 | 				     int port, struct mlx4_cmd_mailbox *mailbox) | 
 | { | 
 | 	struct mlx4_roce_gid_entry *gid_entry_mbox; | 
 | 	struct mlx4_priv *priv = mlx4_priv(dev); | 
 | 	int num_gids, base, offset; | 
 | 	int i, err; | 
 |  | 
 | 	num_gids = mlx4_get_slave_num_gids(dev, slave, port); | 
 | 	base = mlx4_get_base_gid_ix(dev, slave, port); | 
 |  | 
 | 	memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE); | 
 |  | 
 | 	qlock(&(priv->port[port].gid_table.mutex)); | 
 | 	/* Zero-out gids belonging to that slave in the port GID table */ | 
 | 	for (i = 0, offset = base; i < num_gids; offset++, i++) | 
 | 		memcpy(priv->port[port].gid_table.roce_gids[offset].raw, | 
 | 		       zgid_entry.raw, MLX4_ROCE_GID_ENTRY_SIZE); | 
 |  | 
 | 	/* Now, copy roce port gids table to mailbox for passing to FW */ | 
 | 	gid_entry_mbox = (struct mlx4_roce_gid_entry *)mailbox->buf; | 
 | 	for (i = 0; i < MLX4_ROCE_MAX_GIDS; gid_entry_mbox++, i++) | 
 | 		memcpy(gid_entry_mbox->raw, | 
 | 		       priv->port[port].gid_table.roce_gids[i].raw, | 
 | 		       MLX4_ROCE_GID_ENTRY_SIZE); | 
 |  | 
 | 	err = mlx4_cmd(dev, mailbox->dma, | 
 | 		       ((uint32_t)port) | (MLX4_SET_PORT_GID_TABLE << 8), | 
 | 		       MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, | 
 | 		       MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); | 
 | 	qunlock(&(priv->port[port].gid_table.mutex)); | 
 | 	return err; | 
 | } | 
 |  | 
 |  | 
 | void mlx4_reset_roce_gids(struct mlx4_dev *dev, int slave) | 
 | { | 
 | 	struct mlx4_active_ports actv_ports; | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	int num_eth_ports, err; | 
 | 	int i; | 
 |  | 
 | 	if (slave < 0 || slave > dev->persist->num_vfs) | 
 | 		return; | 
 |  | 
 | 	actv_ports = mlx4_get_active_ports(dev, slave); | 
 |  | 
 | 	for (i = 0, num_eth_ports = 0; i < dev->caps.num_ports; i++) { | 
 | 		if (test_bit(i, actv_ports.ports)) { | 
 | 			if (dev->caps.port_type[i + 1] != MLX4_PORT_TYPE_ETH) | 
 | 				continue; | 
 | 			num_eth_ports++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (!num_eth_ports) | 
 | 		return; | 
 |  | 
 | 	/* have ETH ports.  Alloc mailbox for SET_PORT command */ | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return; | 
 |  | 
 | 	for (i = 0; i < dev->caps.num_ports; i++) { | 
 | 		if (test_bit(i, actv_ports.ports)) { | 
 | 			if (dev->caps.port_type[i + 1] != MLX4_PORT_TYPE_ETH) | 
 | 				continue; | 
 | 			err = mlx4_reset_roce_port_gids(dev, slave, i + 1, mailbox); | 
 | 			if (err) | 
 | 				mlx4_warn(dev, "Could not reset ETH port GID table for slave %d, port %d (%d)\n", | 
 | 					  slave, i + 1, err); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return; | 
 | } | 
 |  | 
 | static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, | 
 | 				uint32_t in_mod, | 
 | 				uint8_t op_mod, | 
 | 				struct mlx4_cmd_mailbox *inbox) | 
 | { | 
 | 	struct mlx4_priv *priv = mlx4_priv(dev); | 
 | 	struct mlx4_port_info *port_info; | 
 | 	struct mlx4_mfunc_master_ctx *master = &priv->mfunc.master; | 
 | 	struct mlx4_slave_state *slave_st = &master->slave_state[slave]; | 
 | 	struct mlx4_set_port_rqp_calc_context *qpn_context; | 
 | 	struct mlx4_set_port_general_context *gen_context; | 
 | 	struct mlx4_roce_gid_entry *gid_entry_tbl, *gid_entry_mbox, *gid_entry_mb1; | 
 | 	int reset_qkey_viols; | 
 | 	int port; | 
 | 	int is_eth; | 
 | 	int num_gids; | 
 | 	int base; | 
 | 	uint32_t in_modifier; | 
 | 	uint32_t promisc; | 
 | 	uint16_t mtu, prev_mtu; | 
 | 	int err; | 
 | 	int i, j; | 
 | 	int offset; | 
 | 	__be32 agg_cap_mask; | 
 | 	__be32 slave_cap_mask; | 
 | 	__be32 new_cap_mask; | 
 |  | 
 | 	port = in_mod & 0xff; | 
 | 	in_modifier = in_mod >> 8; | 
 | 	is_eth = op_mod; | 
 | 	port_info = &priv->port[port]; | 
 |  | 
 | 	/* Slaves cannot perform SET_PORT operations except changing MTU */ | 
 | 	if (is_eth) { | 
 | 		if (slave != dev->caps.function && | 
 | 		    in_modifier != MLX4_SET_PORT_GENERAL && | 
 | 		    in_modifier != MLX4_SET_PORT_GID_TABLE) { | 
 | 			mlx4_warn(dev, "denying SET_PORT for slave:%d\n", | 
 | 					slave); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		switch (in_modifier) { | 
 | 		case MLX4_SET_PORT_RQP_CALC: | 
 | 			qpn_context = inbox->buf; | 
 | 			qpn_context->base_qpn = | 
 | 				cpu_to_be32(port_info->base_qpn); | 
 | 			qpn_context->n_mac = 0x7; | 
 | 			promisc = be32_to_cpu(qpn_context->promisc) >> | 
 | 				SET_PORT_PROMISC_SHIFT; | 
 | 			qpn_context->promisc = cpu_to_be32( | 
 | 				promisc << SET_PORT_PROMISC_SHIFT | | 
 | 				port_info->base_qpn); | 
 | 			promisc = be32_to_cpu(qpn_context->mcast) >> | 
 | 				SET_PORT_MC_PROMISC_SHIFT; | 
 | 			qpn_context->mcast = cpu_to_be32( | 
 | 				promisc << SET_PORT_MC_PROMISC_SHIFT | | 
 | 				port_info->base_qpn); | 
 | 			break; | 
 | 		case MLX4_SET_PORT_GENERAL: | 
 | 			gen_context = inbox->buf; | 
 | 			/* Mtu is configured as the max MTU among all the | 
 | 			 * the functions on the port. */ | 
 | 			mtu = be16_to_cpu(gen_context->mtu); | 
 | 			mtu = MIN_T(int, mtu, | 
 | 				    dev->caps.eth_mtu_cap[port] + ETHERHDRSIZE + VLAN_HLEN + ETH_FCS_LEN); | 
 | 			prev_mtu = slave_st->mtu[port]; | 
 | 			slave_st->mtu[port] = mtu; | 
 | 			if (mtu > master->max_mtu[port]) | 
 | 				master->max_mtu[port] = mtu; | 
 | 			if (mtu < prev_mtu && prev_mtu == | 
 | 						master->max_mtu[port]) { | 
 | 				slave_st->mtu[port] = mtu; | 
 | 				master->max_mtu[port] = mtu; | 
 | 				for (i = 0; i < dev->num_slaves; i++) { | 
 | 					master->max_mtu[port] = | 
 | 					MAX(master->max_mtu[port], | 
 | 					    master->slave_state[i].mtu[port]); | 
 | 				} | 
 | 			} | 
 |  | 
 | 			gen_context->mtu = cpu_to_be16(master->max_mtu[port]); | 
 | 			break; | 
 | 		case MLX4_SET_PORT_GID_TABLE: | 
 | 			/* change to MULTIPLE entries: number of guest's gids | 
 | 			 * need a FOR-loop here over number of gids the guest has. | 
 | 			 * 1. Check no duplicates in gids passed by slave | 
 | 			 */ | 
 | 			num_gids = mlx4_get_slave_num_gids(dev, slave, port); | 
 | 			base = mlx4_get_base_gid_ix(dev, slave, port); | 
 | 			gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf); | 
 | 			for (i = 0; i < num_gids; gid_entry_mbox++, i++) { | 
 | 				if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw, | 
 | 					    sizeof(zgid_entry))) | 
 | 					continue; | 
 | 				gid_entry_mb1 = gid_entry_mbox + 1; | 
 | 				for (j = i + 1; j < num_gids; gid_entry_mb1++, j++) { | 
 | 					if (!memcmp(gid_entry_mb1->raw, | 
 | 						    zgid_entry.raw, sizeof(zgid_entry))) | 
 | 						continue; | 
 | 					if (!memcmp(gid_entry_mb1->raw, gid_entry_mbox->raw, | 
 | 						    sizeof(gid_entry_mbox->raw))) { | 
 | 						/* found duplicate */ | 
 | 						return -EINVAL; | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			/* 2. Check that do not have duplicates in OTHER | 
 | 			 *    entries in the port GID table | 
 | 			 */ | 
 |  | 
 | 			qlock(&(priv->port[port].gid_table.mutex)); | 
 | 			for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) { | 
 | 				if (i >= base && i < base + num_gids) | 
 | 					continue; /* don't compare to slave's current gids */ | 
 | 				gid_entry_tbl = &priv->port[port].gid_table.roce_gids[i]; | 
 | 				if (!memcmp(gid_entry_tbl->raw, zgid_entry.raw, sizeof(zgid_entry))) | 
 | 					continue; | 
 | 				gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf); | 
 | 				for (j = 0; j < num_gids; gid_entry_mbox++, j++) { | 
 | 					if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw, | 
 | 						    sizeof(zgid_entry))) | 
 | 						continue; | 
 | 					if (!memcmp(gid_entry_mbox->raw, gid_entry_tbl->raw, | 
 | 						    sizeof(gid_entry_tbl->raw))) { | 
 | 						/* found duplicate */ | 
 | 						mlx4_warn(dev, "requested gid entry for slave:%d is a duplicate of gid at index %d\n", | 
 | 							  slave, i); | 
 | 						qunlock(&(priv->port[port].gid_table.mutex)); | 
 | 						return -EINVAL; | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			/* insert slave GIDs with memcpy, starting at slave's base index */ | 
 | 			gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf); | 
 | 			for (i = 0, offset = base; i < num_gids; gid_entry_mbox++, offset++, i++) | 
 | 				memcpy(priv->port[port].gid_table.roce_gids[offset].raw, | 
 | 				       gid_entry_mbox->raw, MLX4_ROCE_GID_ENTRY_SIZE); | 
 |  | 
 | 			/* Now, copy roce port gids table to current mailbox for passing to FW */ | 
 | 			gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf); | 
 | 			for (i = 0; i < MLX4_ROCE_MAX_GIDS; gid_entry_mbox++, i++) | 
 | 				memcpy(gid_entry_mbox->raw, | 
 | 				       priv->port[port].gid_table.roce_gids[i].raw, | 
 | 				       MLX4_ROCE_GID_ENTRY_SIZE); | 
 |  | 
 | 			err = mlx4_cmd(dev, inbox->dma, in_mod & 0xffff, op_mod, | 
 | 				       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 				       MLX4_CMD_NATIVE); | 
 | 			qunlock(&(priv->port[port].gid_table.mutex)); | 
 | 			return err; | 
 | 		} | 
 |  | 
 | 		return mlx4_cmd(dev, inbox->dma, in_mod & 0xffff, op_mod, | 
 | 				MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 				MLX4_CMD_NATIVE); | 
 | 	} | 
 |  | 
 | 	/* Slaves are not allowed to SET_PORT beacon (LED) blink */ | 
 | 	if (op_mod == MLX4_SET_PORT_BEACON_OPCODE) { | 
 | 		mlx4_warn(dev, "denying SET_PORT Beacon slave:%d\n", slave); | 
 | 		return -EPERM; | 
 | 	} | 
 |  | 
 | 	/* For IB, we only consider: | 
 | 	 * - The capability mask, which is set to the aggregate of all | 
 | 	 *   slave function capabilities | 
 | 	 * - The QKey violatin counter - reset according to each request. | 
 | 	 */ | 
 |  | 
 | 	if (dev->flags & MLX4_FLAG_OLD_PORT_CMDS) { | 
 | 		reset_qkey_viols = (*(uint8_t *) inbox->buf) & 0x40; | 
 | 		new_cap_mask = ((__be32 *) inbox->buf)[2]; | 
 | 	} else { | 
 | 		reset_qkey_viols = ((uint8_t *) inbox->buf)[3] & 0x1; | 
 | 		new_cap_mask = ((__be32 *) inbox->buf)[1]; | 
 | 	} | 
 |  | 
 | 	/* slave may not set the IS_SM capability for the port */ | 
 | 	if (slave != mlx4_master_func_num(dev) && | 
 | 	    (be32_to_cpu(new_cap_mask) & MLX4_PORT_CAP_IS_SM)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* No DEV_MGMT in multifunc mode */ | 
 | 	if (mlx4_is_mfunc(dev) && | 
 | 	    (be32_to_cpu(new_cap_mask) & MLX4_PORT_CAP_DEV_MGMT_SUP)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	agg_cap_mask = 0; | 
 | 	slave_cap_mask = | 
 | 		priv->mfunc.master.slave_state[slave].ib_cap_mask[port]; | 
 | 	priv->mfunc.master.slave_state[slave].ib_cap_mask[port] = new_cap_mask; | 
 | 	for (i = 0; i < dev->num_slaves; i++) | 
 | 		agg_cap_mask |= | 
 | 			priv->mfunc.master.slave_state[i].ib_cap_mask[port]; | 
 |  | 
 | 	/* only clear mailbox for guests.  Master may be setting | 
 | 	* MTU or PKEY table size | 
 | 	*/ | 
 | 	if (slave != dev->caps.function) | 
 | 		memset(inbox->buf, 0, 256); | 
 | 	if (dev->flags & MLX4_FLAG_OLD_PORT_CMDS) { | 
 | 		*(uint8_t *) inbox->buf	   |= !!reset_qkey_viols << 6; | 
 | 		((__be32 *) inbox->buf)[2] = agg_cap_mask; | 
 | 	} else { | 
 | 		((uint8_t *) inbox->buf)[3]     |= !!reset_qkey_viols; | 
 | 		((__be32 *) inbox->buf)[1] = agg_cap_mask; | 
 | 	} | 
 |  | 
 | 	err = mlx4_cmd(dev, inbox->dma, port, is_eth, MLX4_CMD_SET_PORT, | 
 | 		       MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); | 
 | 	if (err) | 
 | 		priv->mfunc.master.slave_state[slave].ib_cap_mask[port] = | 
 | 			slave_cap_mask; | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_SET_PORT_wrapper(struct mlx4_dev *dev, int slave, | 
 | 			  struct mlx4_vhcr *vhcr, | 
 | 			  struct mlx4_cmd_mailbox *inbox, | 
 | 			  struct mlx4_cmd_mailbox *outbox, | 
 | 			  struct mlx4_cmd_info *cmd) | 
 | { | 
 | 	int port = mlx4_slave_convert_port( | 
 | 			dev, slave, vhcr->in_modifier & 0xFF); | 
 |  | 
 | 	if (port < 0) | 
 | 		return -EINVAL; | 
 |  | 
 | 	vhcr->in_modifier = (vhcr->in_modifier & ~0xFF) | | 
 | 			    (port & 0xFF); | 
 |  | 
 | 	return mlx4_common_set_port(dev, slave, vhcr->in_modifier, | 
 | 				    vhcr->op_modifier, inbox); | 
 | } | 
 |  | 
 | /* bit locations for set port command with zero op modifier */ | 
 | enum { | 
 | 	MLX4_SET_PORT_VL_CAP	 = 4, /* bits 7:4 */ | 
 | 	MLX4_SET_PORT_MTU_CAP	 = 12, /* bits 15:12 */ | 
 | 	MLX4_CHANGE_PORT_PKEY_TBL_SZ = 20, | 
 | 	MLX4_CHANGE_PORT_VL_CAP	 = 21, | 
 | 	MLX4_CHANGE_PORT_MTU_CAP = 22, | 
 | }; | 
 |  | 
 | int mlx4_SET_PORT(struct mlx4_dev *dev, uint8_t port, int pkey_tbl_sz) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	int err, vl_cap, pkey_tbl_flag = 0; | 
 |  | 
 | 	if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) | 
 | 		return 0; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 |  | 
 | 	((__be32 *) mailbox->buf)[1] = dev->caps.ib_port_def_cap[port]; | 
 |  | 
 | 	if (pkey_tbl_sz >= 0 && mlx4_is_master(dev)) { | 
 | 		pkey_tbl_flag = 1; | 
 | 		((__be16 *) mailbox->buf)[20] = cpu_to_be16(pkey_tbl_sz); | 
 | 	} | 
 |  | 
 | 	/* IB VL CAP enum isn't used by the firmware, just numerical values */ | 
 | 	for (vl_cap = 8; vl_cap >= 1; vl_cap >>= 1) { | 
 | 		((__be32 *) mailbox->buf)[0] = cpu_to_be32( | 
 | 			(1 << MLX4_CHANGE_PORT_MTU_CAP) | | 
 | 			(1 << MLX4_CHANGE_PORT_VL_CAP)  | | 
 | 			(pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) | | 
 | 			(dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) | | 
 | 			(vl_cap << MLX4_SET_PORT_VL_CAP)); | 
 | 		err = mlx4_cmd(dev, mailbox->dma, port, | 
 | 			       MLX4_SET_PORT_IB_OPCODE, MLX4_CMD_SET_PORT, | 
 | 			       MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); | 
 | 		if (err != -ENOMEM) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_SET_PORT_general(struct mlx4_dev *dev, uint8_t port, int mtu, | 
 | 			  uint8_t pptx, uint8_t pfctx, uint8_t pprx, | 
 | 			  uint8_t pfcrx) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	struct mlx4_set_port_general_context *context; | 
 | 	int err; | 
 | 	uint32_t in_mod; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 | 	context = mailbox->buf; | 
 | 	context->flags = SET_PORT_GEN_ALL_VALID; | 
 | 	context->mtu = cpu_to_be16(mtu); | 
 | 	context->pptx = (pptx * (!pfctx)) << 7; | 
 | 	context->pfctx = pfctx; | 
 | 	context->pprx = (pprx * (!pfcrx)) << 7; | 
 | 	context->pfcrx = pfcrx; | 
 |  | 
 | 	in_mod = MLX4_SET_PORT_GENERAL << 8 | port; | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_WRAPPED); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_PORT_general); | 
 |  | 
 | int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, uint8_t port, | 
 | 			   uint32_t base_qpn, | 
 | 			   uint8_t promisc) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	struct mlx4_set_port_rqp_calc_context *context; | 
 | 	int err; | 
 | 	uint32_t in_mod; | 
 | 	uint32_t m_promisc = (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) ? | 
 | 		MCAST_DIRECT : MCAST_DEFAULT; | 
 |  | 
 | 	if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) | 
 | 		return 0; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 | 	context = mailbox->buf; | 
 | 	context->base_qpn = cpu_to_be32(base_qpn); | 
 | 	context->n_mac = dev->caps.log_num_macs; | 
 | 	context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT | | 
 | 				       base_qpn); | 
 | 	context->mcast = cpu_to_be32(m_promisc << SET_PORT_MC_PROMISC_SHIFT | | 
 | 				     base_qpn); | 
 | 	context->intra_no_vlan = 0; | 
 | 	context->no_vlan = MLX4_NO_VLAN_IDX; | 
 | 	context->intra_vlan_miss = 0; | 
 | 	context->vlan_miss = MLX4_VLAN_MISS_IDX; | 
 |  | 
 | 	in_mod = MLX4_SET_PORT_RQP_CALC << 8 | port; | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_WRAPPED); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc); | 
 |  | 
 | int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, uint8_t port, | 
 | 			    uint8_t ignore_fcs_value) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	struct mlx4_set_port_general_context *context; | 
 | 	uint32_t in_mod; | 
 | 	int err; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 | 	context = mailbox->buf; | 
 | 	context->v_ignore_fcs |= MLX4_FLAG_V_IGNORE_FCS_MASK; | 
 | 	if (ignore_fcs_value) | 
 | 		context->ignore_fcs |= MLX4_IGNORE_FCS_MASK; | 
 | 	else | 
 | 		context->ignore_fcs &= ~MLX4_IGNORE_FCS_MASK; | 
 |  | 
 | 	in_mod = MLX4_SET_PORT_GENERAL << 8 | port; | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, | 
 | 		       MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_PORT_fcs_check); | 
 |  | 
 | enum { | 
 | 	VXLAN_ENABLE_MODIFY	= 1 << 7, | 
 | 	VXLAN_STEERING_MODIFY	= 1 << 6, | 
 |  | 
 | 	VXLAN_ENABLE		= 1 << 7, | 
 | }; | 
 |  | 
 | struct mlx4_set_port_vxlan_context { | 
 | 	uint32_t	reserved1; | 
 | 	uint8_t	modify_flags; | 
 | 	uint8_t	reserved2; | 
 | 	uint8_t	enable_flags; | 
 | 	uint8_t	steering; | 
 | }; | 
 |  | 
 | int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, uint8_t port, uint8_t steering, | 
 | 			int enable) | 
 | { | 
 | 	int err; | 
 | 	uint32_t in_mod; | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 | 	struct mlx4_set_port_vxlan_context  *context; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 | 	context = mailbox->buf; | 
 | 	memset(context, 0, sizeof(*context)); | 
 |  | 
 | 	context->modify_flags = VXLAN_ENABLE_MODIFY | VXLAN_STEERING_MODIFY; | 
 | 	if (enable) | 
 | 		context->enable_flags = VXLAN_ENABLE; | 
 | 	context->steering  = steering; | 
 |  | 
 | 	in_mod = MLX4_SET_PORT_VXLAN << 8 | port; | 
 | 	err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_NATIVE); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_PORT_VXLAN); | 
 |  | 
 | int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, uint8_t port, uint16_t time) | 
 | { | 
 | 	int err; | 
 | 	struct mlx4_cmd_mailbox *mailbox; | 
 |  | 
 | 	mailbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(mailbox)) | 
 | 		return PTR_ERR(mailbox); | 
 |  | 
 | 	*((__be32 *)mailbox->buf) = cpu_to_be32(time); | 
 |  | 
 | 	err = mlx4_cmd(dev, mailbox->dma, port, MLX4_SET_PORT_BEACON_OPCODE, | 
 | 		       MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, | 
 | 		       MLX4_CMD_NATIVE); | 
 |  | 
 | 	mlx4_free_cmd_mailbox(dev, mailbox); | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_PORT_BEACON); | 
 |  | 
 | int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave, | 
 | 				struct mlx4_vhcr *vhcr, | 
 | 				struct mlx4_cmd_mailbox *inbox, | 
 | 				struct mlx4_cmd_mailbox *outbox, | 
 | 				struct mlx4_cmd_info *cmd) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, uint8_t port, | 
 | 			uint64_t mac, uint64_t clear, uint8_t mode) | 
 | { | 
 | 	return mlx4_cmd(dev, (mac | (clear << 63)), port, mode, | 
 | 			MLX4_CMD_SET_MCAST_FLTR, MLX4_CMD_TIME_CLASS_B, | 
 | 			MLX4_CMD_WRAPPED); | 
 | } | 
 | EXPORT_SYMBOL(mlx4_SET_MCAST_FLTR); | 
 |  | 
 | int mlx4_SET_VLAN_FLTR_wrapper(struct mlx4_dev *dev, int slave, | 
 | 			       struct mlx4_vhcr *vhcr, | 
 | 			       struct mlx4_cmd_mailbox *inbox, | 
 | 			       struct mlx4_cmd_mailbox *outbox, | 
 | 			       struct mlx4_cmd_info *cmd) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | int mlx4_common_dump_eth_stats(struct mlx4_dev *dev, int slave, | 
 | 			       uint32_t in_mod, | 
 | 			       struct mlx4_cmd_mailbox *outbox) | 
 | { | 
 | 	return mlx4_cmd_box(dev, 0, outbox->dma, in_mod, 0, | 
 | 			    MLX4_CMD_DUMP_ETH_STATS, MLX4_CMD_TIME_CLASS_B, | 
 | 			    MLX4_CMD_NATIVE); | 
 | } | 
 |  | 
 | int mlx4_DUMP_ETH_STATS_wrapper(struct mlx4_dev *dev, int slave, | 
 | 				struct mlx4_vhcr *vhcr, | 
 | 				struct mlx4_cmd_mailbox *inbox, | 
 | 				struct mlx4_cmd_mailbox *outbox, | 
 | 				struct mlx4_cmd_info *cmd) | 
 | { | 
 | 	if (slave != dev->caps.function) | 
 | 		return 0; | 
 | 	return mlx4_common_dump_eth_stats(dev, slave, | 
 | 					  vhcr->in_modifier, outbox); | 
 | } | 
 |  | 
 | int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, uint8_t *gid, | 
 | 				 int *slave_id) | 
 | { | 
 | 	struct mlx4_priv *priv = mlx4_priv(dev); | 
 | 	int i, found_ix = -1; | 
 | 	int vf_gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS; | 
 | 	struct mlx4_slaves_pport slaves_pport; | 
 | 	unsigned num_vfs; | 
 | 	int slave_gid; | 
 |  | 
 | 	if (!mlx4_is_mfunc(dev)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	slaves_pport = mlx4_phys_to_slaves_pport(dev, port); | 
 | 	num_vfs = bitmap_weight(slaves_pport.slaves, | 
 | 				dev->persist->num_vfs + 1) - 1; | 
 |  | 
 | 	for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) { | 
 | 		if (!memcmp(priv->port[port].gid_table.roce_gids[i].raw, gid, | 
 | 			    MLX4_ROCE_GID_ENTRY_SIZE)) { | 
 | 			found_ix = i; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (found_ix >= 0) { | 
 | 		/* Calculate a slave_gid which is the slave number in the gid | 
 | 		 * table and not a globally unique slave number. | 
 | 		 */ | 
 | 		if (found_ix < MLX4_ROCE_PF_GIDS) | 
 | 			slave_gid = 0; | 
 | 		else if (found_ix < MLX4_ROCE_PF_GIDS + (vf_gids % num_vfs) * | 
 | 			 (vf_gids / num_vfs + 1)) | 
 | 			slave_gid = ((found_ix - MLX4_ROCE_PF_GIDS) / | 
 | 				     (vf_gids / num_vfs + 1)) + 1; | 
 | 		else | 
 | 			slave_gid = | 
 | 			((found_ix - MLX4_ROCE_PF_GIDS - | 
 | 			  ((vf_gids % num_vfs) * ((vf_gids / num_vfs + 1)))) / | 
 | 			 (vf_gids / num_vfs)) + vf_gids % num_vfs + 1; | 
 |  | 
 | 		/* Calculate the globally unique slave id */ | 
 | 		if (slave_gid) { | 
 | 			struct mlx4_active_ports exclusive_ports; | 
 | 			struct mlx4_active_ports actv_ports; | 
 | 			struct mlx4_slaves_pport slaves_pport_actv; | 
 | 			unsigned max_port_p_one; | 
 | 			int num_vfs_before = 0; | 
 | 			int candidate_slave_gid; | 
 |  | 
 | 			/* Calculate how many VFs are on the previous port, if exists */ | 
 | 			for (i = 1; i < port; i++) { | 
 | 				bitmap_zero(exclusive_ports.ports, dev->caps.num_ports); | 
 | 				set_bit(i - 1, exclusive_ports.ports); | 
 | 				slaves_pport_actv = | 
 | 					mlx4_phys_to_slaves_pport_actv( | 
 | 							dev, &exclusive_ports); | 
 | 				num_vfs_before += bitmap_weight( | 
 | 						slaves_pport_actv.slaves, | 
 | 						dev->persist->num_vfs + 1); | 
 | 			} | 
 |  | 
 | 			/* candidate_slave_gid isn't necessarily the correct slave, but | 
 | 			 * it has the same number of ports and is assigned to the same | 
 | 			 * ports as the real slave we're looking for. On dual port VF, | 
 | 			 * slave_gid = [single port VFs on port <port>] + | 
 | 			 * [offset of the current slave from the first dual port VF] + | 
 | 			 * 1 (for the PF). | 
 | 			 */ | 
 | 			candidate_slave_gid = slave_gid + num_vfs_before; | 
 |  | 
 | 			actv_ports = mlx4_get_active_ports(dev, candidate_slave_gid); | 
 | 			max_port_p_one = find_first_bit( | 
 | 				actv_ports.ports, dev->caps.num_ports) + | 
 | 				bitmap_weight(actv_ports.ports, | 
 | 					      dev->caps.num_ports) + 1; | 
 |  | 
 | 			/* Calculate the real slave number */ | 
 | 			for (i = 1; i < max_port_p_one; i++) { | 
 | 				if (i == port) | 
 | 					continue; | 
 | 				bitmap_zero(exclusive_ports.ports, | 
 | 					    dev->caps.num_ports); | 
 | 				set_bit(i - 1, exclusive_ports.ports); | 
 | 				slaves_pport_actv = | 
 | 					mlx4_phys_to_slaves_pport_actv( | 
 | 						dev, &exclusive_ports); | 
 | 				slave_gid += bitmap_weight( | 
 | 						slaves_pport_actv.slaves, | 
 | 						dev->persist->num_vfs + 1); | 
 | 			} | 
 | 		} | 
 | 		*slave_id = slave_gid; | 
 | 	} | 
 |  | 
 | 	return (found_ix >= 0) ? 0 : -EINVAL; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_get_slave_from_roce_gid); | 
 |  | 
 | int mlx4_get_roce_gid_from_slave(struct mlx4_dev *dev, int port, int slave_id, | 
 | 				 uint8_t *gid) | 
 | { | 
 | 	struct mlx4_priv *priv = mlx4_priv(dev); | 
 |  | 
 | 	if (!mlx4_is_master(dev)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	memcpy(gid, priv->port[port].gid_table.roce_gids[slave_id].raw, | 
 | 	       MLX4_ROCE_GID_ENTRY_SIZE); | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_get_roce_gid_from_slave); | 
 |  | 
 | /* Cable Module Info */ | 
 | #define MODULE_INFO_MAX_READ 48 | 
 |  | 
 | #define I2C_ADDR_LOW  0x50 | 
 | #define I2C_ADDR_HIGH 0x51 | 
 | #define I2C_PAGE_SIZE 256 | 
 |  | 
 | /* Module Info Data */ | 
 | struct mlx4_cable_info { | 
 | 	uint8_t	i2c_addr; | 
 | 	uint8_t	page_num; | 
 | 	__be16	dev_mem_address; | 
 | 	__be16	reserved1; | 
 | 	__be16	size; | 
 | 	__be32	reserved2[2]; | 
 | 	uint8_t	data[MODULE_INFO_MAX_READ]; | 
 | }; | 
 |  | 
 | enum cable_info_err { | 
 | 	 CABLE_INF_INV_PORT      = 0x1, | 
 | 	 CABLE_INF_OP_NOSUP      = 0x2, | 
 | 	 CABLE_INF_NOT_CONN      = 0x3, | 
 | 	 CABLE_INF_NO_EEPRM      = 0x4, | 
 | 	 CABLE_INF_PAGE_ERR      = 0x5, | 
 | 	 CABLE_INF_INV_ADDR      = 0x6, | 
 | 	 CABLE_INF_I2C_ADDR      = 0x7, | 
 | 	 CABLE_INF_QSFP_VIO      = 0x8, | 
 | 	 CABLE_INF_I2C_BUSY      = 0x9, | 
 | }; | 
 |  | 
 | #define MAD_STATUS_2_CABLE_ERR(mad_status) ((mad_status >> 8) & 0xFF) | 
 |  | 
 | static inline const char *cable_info_mad_err_str(uint16_t mad_status) | 
 | { | 
 | 	uint8_t err = MAD_STATUS_2_CABLE_ERR(mad_status); | 
 |  | 
 | 	switch (err) { | 
 | 	case CABLE_INF_INV_PORT: | 
 | 		return "invalid port selected"; | 
 | 	case CABLE_INF_OP_NOSUP: | 
 | 		return "operation not supported for this port (the port is of type CX4 or internal)"; | 
 | 	case CABLE_INF_NOT_CONN: | 
 | 		return "cable is not connected"; | 
 | 	case CABLE_INF_NO_EEPRM: | 
 | 		return "the connected cable has no EPROM (passive copper cable)"; | 
 | 	case CABLE_INF_PAGE_ERR: | 
 | 		return "page number is greater than 15"; | 
 | 	case CABLE_INF_INV_ADDR: | 
 | 		return "invalid device_address or size (that is, size equals 0 or address+size is greater than 256)"; | 
 | 	case CABLE_INF_I2C_ADDR: | 
 | 		return "invalid I2C slave address"; | 
 | 	case CABLE_INF_QSFP_VIO: | 
 | 		return "at least one cable violates the QSFP specification and ignores the modsel signal"; | 
 | 	case CABLE_INF_I2C_BUSY: | 
 | 		return "I2C bus is constantly busy"; | 
 | 	} | 
 | 	return "Unknown Error"; | 
 | } | 
 |  | 
 | /** | 
 |  * mlx4_get_module_info - Read cable module eeprom data | 
 |  * @dev: mlx4_dev. | 
 |  * @port: port number. | 
 |  * @offset: byte offset in eeprom to start reading data from. | 
 |  * @size: num of bytes to read. | 
 |  * @data: output buffer to put the requested data into. | 
 |  * | 
 |  * Reads cable module eeprom data, puts the outcome data into | 
 |  * data pointer paramer. | 
 |  * Returns num of read bytes on success or a negative error | 
 |  * code. | 
 |  */ | 
 | int mlx4_get_module_info(struct mlx4_dev *dev, uint8_t port, | 
 | 			 uint16_t offset, uint16_t size, uint8_t *data) | 
 | { | 
 | 	struct mlx4_cmd_mailbox *inbox, *outbox; | 
 | 	struct mlx4_mad_ifc *inmad, *outmad; | 
 | 	struct mlx4_cable_info *cable_info; | 
 | 	uint16_t i2c_addr; | 
 | 	int ret; | 
 |  | 
 | 	if (size > MODULE_INFO_MAX_READ) | 
 | 		size = MODULE_INFO_MAX_READ; | 
 |  | 
 | 	inbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(inbox)) | 
 | 		return PTR_ERR(inbox); | 
 |  | 
 | 	outbox = mlx4_alloc_cmd_mailbox(dev); | 
 | 	if (IS_ERR(outbox)) { | 
 | 		mlx4_free_cmd_mailbox(dev, inbox); | 
 | 		return PTR_ERR(outbox); | 
 | 	} | 
 |  | 
 | 	inmad = (struct mlx4_mad_ifc *)(inbox->buf); | 
 | 	outmad = (struct mlx4_mad_ifc *)(outbox->buf); | 
 |  | 
 | 	inmad->method = 0x1; /* Get */ | 
 | 	inmad->class_version = 0x1; | 
 | 	inmad->mgmt_class = 0x1; | 
 | 	inmad->base_version = 0x1; | 
 | 	inmad->attr_id = cpu_to_be16(0xFF60); /* Module Info */ | 
 |  | 
 | 	if (offset < I2C_PAGE_SIZE && offset + size > I2C_PAGE_SIZE) | 
 | 		/* Cross pages reads are not allowed | 
 | 		 * read until offset 256 in low page | 
 | 		 */ | 
 | 		size -= offset + size - I2C_PAGE_SIZE; | 
 |  | 
 | 	i2c_addr = I2C_ADDR_LOW; | 
 | 	if (offset >= I2C_PAGE_SIZE) { | 
 | 		/* Reset offset to high page */ | 
 | 		i2c_addr = I2C_ADDR_HIGH; | 
 | 		offset -= I2C_PAGE_SIZE; | 
 | 	} | 
 |  | 
 | 	cable_info = (struct mlx4_cable_info *)inmad->data; | 
 | 	cable_info->dev_mem_address = cpu_to_be16(offset); | 
 | 	cable_info->page_num = 0; | 
 | 	cable_info->i2c_addr = i2c_addr; | 
 | 	cable_info->size = cpu_to_be16(size); | 
 |  | 
 | 	ret = mlx4_cmd_box(dev, inbox->dma, outbox->dma, port, 3, | 
 | 			   MLX4_CMD_MAD_IFC, MLX4_CMD_TIME_CLASS_C, | 
 | 			   MLX4_CMD_NATIVE); | 
 | 	if (ret) | 
 | 		goto out; | 
 |  | 
 | 	if (be16_to_cpu(outmad->status)) { | 
 | 		/* Mad returned with bad status */ | 
 | 		ret = be16_to_cpu(outmad->status); | 
 | 		mlx4_warn(dev, | 
 | 			  "MLX4_CMD_MAD_IFC Get Module info attr(%x) port(%d) i2c_addr(%x) offset(%d) size(%d): Response Mad Status(%x) - %s\n", | 
 | 			  0xFF60, port, i2c_addr, offset, size, | 
 | 			  ret, cable_info_mad_err_str(ret)); | 
 |  | 
 | 		if (i2c_addr == I2C_ADDR_HIGH && | 
 | 		    MAD_STATUS_2_CABLE_ERR(ret) == CABLE_INF_I2C_ADDR) | 
 | 			/* Some SFP cables do not support i2c slave | 
 | 			 * address 0x51 (high page), abort silently. | 
 | 			 */ | 
 | 			ret = 0; | 
 | 		else | 
 | 			ret = -ret; | 
 | 		goto out; | 
 | 	} | 
 | 	cable_info = (struct mlx4_cable_info *)outmad->data; | 
 | 	memcpy(data, cable_info->data, size); | 
 | 	ret = size; | 
 | out: | 
 | 	mlx4_free_cmd_mailbox(dev, inbox); | 
 | 	mlx4_free_cmd_mailbox(dev, outbox); | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL(mlx4_get_module_info); |