| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* Copyright (c) 2016 - 2021 Intel Corporation */ |
| #include <linux/etherdevice.h> |
| |
| #include "osdep.h" |
| #include "hmc.h" |
| #include "defs.h" |
| #include "type.h" |
| #include "protos.h" |
| #include "uda.h" |
| #include "uda_d.h" |
| |
| /** |
| * irdma_sc_access_ah() - Create, modify or delete AH |
| * @cqp: struct for cqp hw |
| * @info: ah information |
| * @op: Operation |
| * @scratch: u64 saved to be used during cqp completion |
| */ |
| int irdma_sc_access_ah(struct irdma_sc_cqp *cqp, struct irdma_ah_info *info, |
| u32 op, u64 scratch) |
| { |
| __le64 *wqe; |
| u64 qw1, qw2; |
| |
| wqe = irdma_sc_cqp_get_next_send_wqe(cqp, scratch); |
| if (!wqe) |
| return -ENOMEM; |
| |
| set_64bit_val(wqe, 0, ether_addr_to_u64(info->mac_addr) << 16); |
| qw1 = FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_PDINDEXLO, info->pd_idx) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_TC, info->tc_tos) | |
| FIELD_PREP(IRDMA_UDAQPC_VLANTAG, info->vlan_tag); |
| |
| qw2 = FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ARPINDEX, info->dst_arpindex) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_FLOWLABEL, info->flow_label) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_HOPLIMIT, info->hop_ttl) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_PDINDEXHI, info->pd_idx >> 16); |
| |
| if (!info->ipv4_valid) { |
| set_64bit_val(wqe, 40, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR0, info->dest_ip_addr[0]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR1, info->dest_ip_addr[1])); |
| set_64bit_val(wqe, 32, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR2, info->dest_ip_addr[2]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->dest_ip_addr[3])); |
| |
| set_64bit_val(wqe, 56, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR0, info->src_ip_addr[0]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR1, info->src_ip_addr[1])); |
| set_64bit_val(wqe, 48, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR2, info->src_ip_addr[2]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->src_ip_addr[3])); |
| } else { |
| set_64bit_val(wqe, 32, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->dest_ip_addr[0])); |
| |
| set_64bit_val(wqe, 48, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->src_ip_addr[0])); |
| } |
| |
| set_64bit_val(wqe, 8, qw1); |
| set_64bit_val(wqe, 16, qw2); |
| |
| dma_wmb(); /* need write block before writing WQE header */ |
| |
| set_64bit_val( |
| wqe, 24, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_WQEVALID, cqp->polarity) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_OPCODE, op) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_DOLOOPBACKK, info->do_lpbk) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_IPV4VALID, info->ipv4_valid) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_AVIDX, info->ah_idx) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_INSERTVLANTAG, info->insert_vlan_tag)); |
| |
| print_hex_dump_debug("WQE: MANAGE_AH WQE", DUMP_PREFIX_OFFSET, 16, 8, |
| wqe, IRDMA_CQP_WQE_SIZE * 8, false); |
| irdma_sc_cqp_post_sq(cqp); |
| |
| return 0; |
| } |
| |
| /** |
| * irdma_create_mg_ctx() - create a mcg context |
| * @info: multicast group context info |
| */ |
| static void irdma_create_mg_ctx(struct irdma_mcast_grp_info *info) |
| { |
| struct irdma_mcast_grp_ctx_entry_info *entry_info = NULL; |
| u8 idx = 0; /* index in the array */ |
| u8 ctx_idx = 0; /* index in the MG context */ |
| |
| memset(info->dma_mem_mc.va, 0, IRDMA_MAX_MGS_PER_CTX * sizeof(u64)); |
| |
| for (idx = 0; idx < IRDMA_MAX_MGS_PER_CTX; idx++) { |
| entry_info = &info->mg_ctx_info[idx]; |
| if (entry_info->valid_entry) { |
| set_64bit_val((__le64 *)info->dma_mem_mc.va, |
| ctx_idx * sizeof(u64), |
| FIELD_PREP(IRDMA_UDA_MGCTX_DESTPORT, entry_info->dest_port) | |
| FIELD_PREP(IRDMA_UDA_MGCTX_VALIDENT, entry_info->valid_entry) | |
| FIELD_PREP(IRDMA_UDA_MGCTX_QPID, entry_info->qp_id)); |
| ctx_idx++; |
| } |
| } |
| } |
| |
| /** |
| * irdma_access_mcast_grp() - Access mcast group based on op |
| * @cqp: Control QP |
| * @info: multicast group context info |
| * @op: operation to perform |
| * @scratch: u64 saved to be used during cqp completion |
| */ |
| int irdma_access_mcast_grp(struct irdma_sc_cqp *cqp, |
| struct irdma_mcast_grp_info *info, u32 op, |
| u64 scratch) |
| { |
| __le64 *wqe; |
| |
| if (info->mg_id >= IRDMA_UDA_MAX_FSI_MGS) { |
| ibdev_dbg(to_ibdev(cqp->dev), "WQE: mg_id out of range\n"); |
| return -EINVAL; |
| } |
| |
| wqe = irdma_sc_cqp_get_next_send_wqe(cqp, scratch); |
| if (!wqe) { |
| ibdev_dbg(to_ibdev(cqp->dev), "WQE: ring full\n"); |
| return -ENOMEM; |
| } |
| |
| irdma_create_mg_ctx(info); |
| |
| set_64bit_val(wqe, 32, info->dma_mem_mc.pa); |
| set_64bit_val(wqe, 16, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_VLANID, info->vlan_id) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_QS_HANDLE, info->qs_handle)); |
| set_64bit_val(wqe, 0, ether_addr_to_u64(info->dest_mac_addr)); |
| set_64bit_val(wqe, 8, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_HMC_FCN_ID, info->hmc_fcn_id)); |
| |
| if (!info->ipv4_valid) { |
| set_64bit_val(wqe, 56, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR0, info->dest_ip_addr[0]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR1, info->dest_ip_addr[1])); |
| set_64bit_val(wqe, 48, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR2, info->dest_ip_addr[2]) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->dest_ip_addr[3])); |
| } else { |
| set_64bit_val(wqe, 48, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MAV_ADDR3, info->dest_ip_addr[0])); |
| } |
| |
| dma_wmb(); /* need write memory block before writing the WQE header. */ |
| |
| set_64bit_val(wqe, 24, |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_WQEVALID, cqp->polarity) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_OPCODE, op) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_MGIDX, info->mg_id) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_VLANVALID, info->vlan_valid) | |
| FIELD_PREP(IRDMA_UDA_CQPSQ_MG_IPV4VALID, info->ipv4_valid)); |
| |
| print_hex_dump_debug("WQE: MANAGE_MCG WQE", DUMP_PREFIX_OFFSET, 16, 8, |
| wqe, IRDMA_CQP_WQE_SIZE * 8, false); |
| print_hex_dump_debug("WQE: MCG_HOST CTX WQE", DUMP_PREFIX_OFFSET, 16, |
| 8, info->dma_mem_mc.va, |
| IRDMA_MAX_MGS_PER_CTX * 8, false); |
| irdma_sc_cqp_post_sq(cqp); |
| |
| return 0; |
| } |
| |
| /** |
| * irdma_compare_mgs - Compares two multicast group structures |
| * @entry1: Multcast group info |
| * @entry2: Multcast group info in context |
| */ |
| static bool irdma_compare_mgs(struct irdma_mcast_grp_ctx_entry_info *entry1, |
| struct irdma_mcast_grp_ctx_entry_info *entry2) |
| { |
| if (entry1->dest_port == entry2->dest_port && |
| entry1->qp_id == entry2->qp_id) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * irdma_sc_add_mcast_grp - Allocates mcast group entry in ctx |
| * @ctx: Multcast group context |
| * @mg: Multcast group info |
| */ |
| int irdma_sc_add_mcast_grp(struct irdma_mcast_grp_info *ctx, |
| struct irdma_mcast_grp_ctx_entry_info *mg) |
| { |
| u32 idx; |
| bool free_entry_found = false; |
| u32 free_entry_idx = 0; |
| |
| /* find either an identical or a free entry for a multicast group */ |
| for (idx = 0; idx < IRDMA_MAX_MGS_PER_CTX; idx++) { |
| if (ctx->mg_ctx_info[idx].valid_entry) { |
| if (irdma_compare_mgs(&ctx->mg_ctx_info[idx], mg)) { |
| ctx->mg_ctx_info[idx].use_cnt++; |
| return 0; |
| } |
| continue; |
| } |
| if (!free_entry_found) { |
| free_entry_found = true; |
| free_entry_idx = idx; |
| } |
| } |
| |
| if (free_entry_found) { |
| ctx->mg_ctx_info[free_entry_idx] = *mg; |
| ctx->mg_ctx_info[free_entry_idx].valid_entry = true; |
| ctx->mg_ctx_info[free_entry_idx].use_cnt = 1; |
| ctx->no_of_mgs++; |
| return 0; |
| } |
| |
| return -ENOMEM; |
| } |
| |
| /** |
| * irdma_sc_del_mcast_grp - Delete mcast group |
| * @ctx: Multcast group context |
| * @mg: Multcast group info |
| * |
| * Finds and removes a specific mulicast group from context, all |
| * parameters must match to remove a multicast group. |
| */ |
| int irdma_sc_del_mcast_grp(struct irdma_mcast_grp_info *ctx, |
| struct irdma_mcast_grp_ctx_entry_info *mg) |
| { |
| u32 idx; |
| |
| /* find an entry in multicast group context */ |
| for (idx = 0; idx < IRDMA_MAX_MGS_PER_CTX; idx++) { |
| if (!ctx->mg_ctx_info[idx].valid_entry) |
| continue; |
| |
| if (irdma_compare_mgs(mg, &ctx->mg_ctx_info[idx])) { |
| ctx->mg_ctx_info[idx].use_cnt--; |
| |
| if (!ctx->mg_ctx_info[idx].use_cnt) { |
| ctx->mg_ctx_info[idx].valid_entry = false; |
| ctx->no_of_mgs--; |
| /* Remove gap if element was not the last */ |
| if (idx != ctx->no_of_mgs && |
| ctx->no_of_mgs > 0) { |
| memcpy(&ctx->mg_ctx_info[idx], |
| &ctx->mg_ctx_info[ctx->no_of_mgs - 1], |
| sizeof(ctx->mg_ctx_info[idx])); |
| ctx->mg_ctx_info[ctx->no_of_mgs - 1].valid_entry = false; |
| } |
| } |
| |
| return 0; |
| } |
| } |
| |
| return -EINVAL; |
| } |