// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include "hinic_nic_io.h" #include "hinic_qp.h" #define BUF_DESC_SHIFT 1 #define BUF_DESC_SIZE(nr_descs) (((u32)nr_descs) << BUF_DESC_SHIFT) void hinic_prepare_sq_ctrl(struct hinic_sq_ctrl *ctrl, u32 queue_info, int nr_descs, u8 owner) { u32 ctrl_size, task_size, bufdesc_size; ctrl_size = SIZE_8BYTES(sizeof(struct hinic_sq_ctrl)); task_size = SIZE_8BYTES(sizeof(struct hinic_sq_task)); bufdesc_size = BUF_DESC_SIZE(nr_descs); ctrl->ctrl_fmt = SQ_CTRL_SET(bufdesc_size, BUFDESC_SECT_LEN) | SQ_CTRL_SET(task_size, TASKSECT_LEN) | SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) | SQ_CTRL_SET(ctrl_size, LEN) | SQ_CTRL_SET(owner, OWNER); ctrl->ctrl_fmt = be32_to_cpu(ctrl->ctrl_fmt); ctrl->queue_info = queue_info; ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(1U, UC); if (!SQ_CTRL_QUEUE_INFO_GET(ctrl->queue_info, MSS)) { ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(TX_MSS_DEFAULT, MSS); } else if (SQ_CTRL_QUEUE_INFO_GET(ctrl->queue_info, MSS) < TX_MSS_MIN) { /* mss should not less than 80 */ ctrl->queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(ctrl->queue_info, MSS); ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(TX_MSS_MIN, MSS); } ctrl->queue_info = be32_to_cpu(ctrl->queue_info); } int hinic_get_rx_done(struct hinic_rq_cqe *cqe) { u32 status; int rx_done; status = be32_to_cpu(cqe->status); rx_done = RQ_CQE_STATUS_GET(status, RXDONE); if (!rx_done) return 0; return 1; } void hinic_clear_rx_done(struct hinic_rq_cqe *cqe, u32 status_old) { u32 status; status = RQ_CQE_STATUS_CLEAR(status_old, RXDONE); cqe->status = cpu_to_be32(status); /* Make sure Rxdone has been set */ wmb(); } int hinic_get_super_cqe_en(struct hinic_rq_cqe *cqe) { u32 pkt_info; int super_cqe_en; pkt_info = be32_to_cpu(cqe->pkt_info); super_cqe_en = RQ_CQE_SUPER_CQE_EN_GET(pkt_info, SUPER_CQE_EN); if (!super_cqe_en) return 0; return 1; } u32 hinic_get_pkt_len(struct hinic_rq_cqe *cqe) { u32 vlan_len = be32_to_cpu(cqe->vlan_len); return RQ_CQE_SGE_GET(vlan_len, LEN); } u32 hinic_get_pkt_num(struct hinic_rq_cqe *cqe) { u32 pkt_num = be32_to_cpu(cqe->pkt_info); return RQ_CQE_PKT_NUM_GET(pkt_num, NUM); } u32 hinic_get_pkt_len_for_super_cqe(struct hinic_rq_cqe *cqe, bool last) { u32 pkt_len = be32_to_cpu(cqe->pkt_info); if (!last) return RQ_CQE_PKT_LEN_GET(pkt_len, FIRST_LEN); else return RQ_CQE_PKT_LEN_GET(pkt_len, LAST_LEN); } void hinic_prepare_rq_wqe(void *wqe, u16 pi, dma_addr_t buf_addr, dma_addr_t cqe_dma) { struct hinic_rq_wqe *rq_wqe = (struct hinic_rq_wqe *)wqe; struct hinic_rq_ctrl *ctrl = &rq_wqe->ctrl; struct hinic_rq_cqe_sect *cqe_sect = &rq_wqe->cqe_sect; struct hinic_rq_bufdesc *buf_desc = &rq_wqe->buf_desc; u32 rq_ceq_len = sizeof(struct hinic_rq_cqe); ctrl->ctrl_fmt = RQ_CTRL_SET(SIZE_8BYTES(sizeof(*ctrl)), LEN) | RQ_CTRL_SET(SIZE_8BYTES(sizeof(*cqe_sect)), COMPLETE_LEN) | RQ_CTRL_SET(SIZE_8BYTES(sizeof(*buf_desc)), BUFDESC_SECT_LEN) | RQ_CTRL_SET(RQ_COMPLETE_SGE, COMPLETE_FORMAT); hinic_set_sge(&cqe_sect->sge, cqe_dma, rq_ceq_len); buf_desc->addr_high = upper_32_bits(buf_addr); buf_desc->addr_low = lower_32_bits(buf_addr); } void hinic_set_cs_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset) { u32 tcp_udp_cs = 0, sctp = 0; u32 mss = TX_MSS_DEFAULT; /* tcp_udp_cs should be setted to calculate outter checksum when vxlan * packets without inner l3 and l4 */ if (unlikely(l4_offload == SCTP_OFFLOAD_ENABLE)) sctp = 1; else tcp_udp_cs = 1; task->pkt_info0 |= SQ_TASK_INFO0_SET(l4_offload, L4OFFLOAD); task->pkt_info1 |= SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(offset, PLDOFF) | SQ_CTRL_QUEUE_INFO_SET(tcp_udp_cs, TCPUDP_CS) | SQ_CTRL_QUEUE_INFO_SET(sctp, SCTP); *queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(*queue_info, MSS); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(mss, MSS); } void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset, u32 ip_ident, u32 mss) { u32 tso = 0, ufo = 0; if (l4_offload == TCP_OFFLOAD_ENABLE) tso = 1; else if (l4_offload == UDP_OFFLOAD_ENABLE) ufo = 1; task->ufo_v6_identify = be32_to_cpu(ip_ident); /* just keep the same code style here */ task->pkt_info0 |= SQ_TASK_INFO0_SET(l4_offload, L4OFFLOAD); task->pkt_info0 |= SQ_TASK_INFO0_SET(tso || ufo, TSO_UFO); task->pkt_info1 |= SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(offset, PLDOFF) | SQ_CTRL_QUEUE_INFO_SET(tso, TSO) | SQ_CTRL_QUEUE_INFO_SET(ufo, UFO) | SQ_CTRL_QUEUE_INFO_SET(!!l4_offload, TCPUDP_CS); /* cs must be calculate by hw if tso is enable */ *queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(*queue_info, MSS); /* qsf was initialized in prepare_sq_wqe */ *queue_info |= SQ_CTRL_QUEUE_INFO_SET(mss, MSS); } void hinic_set_vlan_tx_offload(struct hinic_sq_task *task, u32 *queue_info, u16 vlan_tag, u16 vlan_pri) { task->pkt_info0 |= SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) | SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(vlan_pri, PRI); } void hinic_task_set_tx_offload_valid(struct hinic_sq_task *task, u32 l2hdr_len) { task->pkt_info0 |= SQ_TASK_INFO0_SET(l2hdr_len, L2HDR_LEN); }