From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alan Brady Date: Thu, 27 Jan 2022 16:09:54 -0800 Subject: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init In-Reply-To: <20220128001009.721392-1-alan.brady@intel.com> References: <20220128001009.721392-1-alan.brady@intel.com> Message-ID: <20220128001009.721392-5-alan.brady@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: intel-wired-lan@osuosl.org List-ID: Initializing device registers is offloaded into function pointers given to iecm from the dependent device driver for a given device, as offsets can vary wildly. This also adds everything needed to setup and use a controlq which uses some of those registers. At the end of probe we kicked off a hard reset and this implements what's needed to handle that reset and continue init. Signed-off-by: Phani Burra Signed-off-by: Joshua Hay Signed-off-by: Madhu Chittim Signed-off-by: Pavan Kumar Linga Signed-off-by: Alan Brady --- drivers/net/ethernet/intel/iecm/Makefile | 3 + .../net/ethernet/intel/iecm/iecm_controlq.c | 649 ++++++++++++++++++ .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++ drivers/net/ethernet/intel/iecm/iecm_lib.c | 191 +++++- .../net/ethernet/intel/iecm/iecm_virtchnl.c | 172 +++++ drivers/net/ethernet/intel/include/iecm.h | 52 ++ .../ethernet/intel/include/iecm_controlq.h | 117 ++++ .../intel/include/iecm_controlq_api.h | 185 +++++ drivers/net/ethernet/intel/include/iecm_mem.h | 20 + 9 files changed, 1563 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq_api.h create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile index 4f497723419d..db8fecb075a6 100644 --- a/drivers/net/ethernet/intel/iecm/Makefile +++ b/drivers/net/ethernet/intel/iecm/Makefile @@ -11,4 +11,7 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include iecm-y := \ iecm_lib.o \ + iecm_virtchnl.o \ + iecm_controlq.o \ + iecm_controlq_setup.o \ iecm_main.o diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq.c b/drivers/net/ethernet/intel/iecm/iecm_controlq.c new file mode 100644 index 000000000000..f9682a7b3e44 --- /dev/null +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, Intel Corporation. */ + +#include "iecm_controlq.h" + +/** + * iecm_ctlq_setup_regs - initialize control queue registers + * @cq: pointer to the specific control queue + * @q_create_info: structs containing info for each queue to be initialized + */ +static void +iecm_ctlq_setup_regs(struct iecm_ctlq_info *cq, + struct iecm_ctlq_create_info *q_create_info) +{ + /* set head and tail registers in our local struct */ + cq->reg.head = q_create_info->reg.head; + cq->reg.tail = q_create_info->reg.tail; + cq->reg.len = q_create_info->reg.len; + cq->reg.bah = q_create_info->reg.bah; + cq->reg.bal = q_create_info->reg.bal; + cq->reg.len_mask = q_create_info->reg.len_mask; + cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask; + cq->reg.head_mask = q_create_info->reg.head_mask; +} + +/** + * iecm_ctlq_init_regs - Initialize control queue registers + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * @is_rxq: true if receive control queue, false otherwise + * + * Initialize registers. The caller is expected to have already initialized the + * descriptor ring memory and buffer memory + */ +static void iecm_ctlq_init_regs(struct iecm_hw *hw, struct iecm_ctlq_info *cq, + bool is_rxq) +{ + /* Update tail to post pre-allocated buffers for rx queues */ + if (is_rxq) + wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1)); + + /* For non-Mailbox control queues only TAIL need to be set */ + if (cq->q_id != -1) + return; + + /* Clear Head for both send or receive */ + wr32(hw, cq->reg.head, 0); + + /* set starting point */ + wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa)); + wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa)); + wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask)); +} + +/** + * iecm_ctlq_init_rxq_bufs - populate receive queue descriptors with buf + * @cq: pointer to the specific Control queue + * + * Record the address of the receive queue DMA buffers in the descriptors. + * The buffers must have been previously allocated. + */ +static void iecm_ctlq_init_rxq_bufs(struct iecm_ctlq_info *cq) +{ + int i = 0; + + for (i = 0; i < cq->ring_size; i++) { + struct iecm_ctlq_desc *desc = IECM_CTLQ_DESC(cq, i); + struct iecm_dma_mem *bi = cq->bi.rx_buff[i]; + + /* No buffer to post to descriptor, continue */ + if (!bi) + continue; + + desc->flags = + cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD); + desc->opcode = 0; + desc->datalen = (__le16)cpu_to_le16(bi->size); + desc->ret_val = 0; + desc->cookie_high = 0; + desc->cookie_low = 0; + desc->params.indirect.addr_high = + cpu_to_le32(upper_32_bits(bi->pa)); + desc->params.indirect.addr_low = + cpu_to_le32(lower_32_bits(bi->pa)); + desc->params.indirect.param0 = 0; + desc->params.indirect.param1 = 0; + } +} + +/** + * iecm_ctlq_shutdown - shutdown the CQ + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * + * The main shutdown routine for any controq queue + */ +static void iecm_ctlq_shutdown(struct iecm_hw *hw, struct iecm_ctlq_info *cq) +{ + mutex_lock(&cq->cq_lock); + + if (!cq->ring_size) + goto shutdown_sq_out; + + /* free ring buffers and the ring itself */ + iecm_ctlq_dealloc_ring_res(hw, cq); + + /* Set ring_size to 0 to indicate uninitialized queue */ + cq->ring_size = 0; + +shutdown_sq_out: + mutex_unlock(&cq->cq_lock); + mutex_destroy(&cq->cq_lock); +} + +/** + * iecm_ctlq_add - add one control queue + * @hw: pointer to hardware struct + * @qinfo: info for queue to be created + * @cq_out: (output) double pointer to control queue to be created + * + * Allocate and initialize a control queue and add it to the control queue list. + * The cq parameter will be allocated/initialized and passed back to the caller + * if no errors occur. + * + * Note: iecm_ctlq_init must be called prior to any calls to iecm_ctlq_add + */ +int iecm_ctlq_add(struct iecm_hw *hw, + struct iecm_ctlq_create_info *qinfo, + struct iecm_ctlq_info **cq_out) +{ + bool is_rxq = false; + int status = 0; + + if (!qinfo->len || !qinfo->buf_size || + qinfo->len > IECM_CTLQ_MAX_RING_SIZE || + qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN) + return -EINVAL; + + *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL); + if (!(*cq_out)) + return -ENOMEM; + + (*cq_out)->cq_type = qinfo->type; + (*cq_out)->q_id = qinfo->id; + (*cq_out)->buf_size = qinfo->buf_size; + (*cq_out)->ring_size = qinfo->len; + + (*cq_out)->next_to_use = 0; + (*cq_out)->next_to_clean = 0; + (*cq_out)->next_to_post = (*cq_out)->ring_size - 1; + + switch (qinfo->type) { + case IECM_CTLQ_TYPE_MAILBOX_RX: + is_rxq = true; + fallthrough; + case IECM_CTLQ_TYPE_MAILBOX_TX: + status = iecm_ctlq_alloc_ring_res(hw, *cq_out); + break; + default: + status = -EBADR; + break; + } + + if (status) + goto init_free_q; + + if (is_rxq) { + iecm_ctlq_init_rxq_bufs(*cq_out); + } else { + /* Allocate the array of msg pointers for TX queues */ + (*cq_out)->bi.tx_msg = kcalloc(qinfo->len, + sizeof(struct iecm_ctlq_msg *), + GFP_KERNEL); + if (!(*cq_out)->bi.tx_msg) { + status = -ENOMEM; + goto init_dealloc_q_mem; + } + } + + iecm_ctlq_setup_regs(*cq_out, qinfo); + + iecm_ctlq_init_regs(hw, *cq_out, is_rxq); + + mutex_init(&(*cq_out)->cq_lock); + + list_add(&(*cq_out)->cq_list, &hw->cq_list_head); + + return status; + +init_dealloc_q_mem: + /* free ring buffers and the ring itself */ + iecm_ctlq_dealloc_ring_res(hw, *cq_out); +init_free_q: + kfree(*cq_out); + + return status; +} + +/** + * iecm_ctlq_remove - deallocate and remove specified control queue + * @hw: pointer to hardware struct + * @cq: pointer to control queue to be removed + */ +void iecm_ctlq_remove(struct iecm_hw *hw, + struct iecm_ctlq_info *cq) +{ + list_del(&cq->cq_list); + iecm_ctlq_shutdown(hw, cq); + kfree(cq); +} + +/** + * iecm_ctlq_init - main initialization routine for all control queues + * @hw: pointer to hardware struct + * @num_q: number of queues to initialize + * @q_info: array of structs containing info for each queue to be initialized + * + * This initializes any number and any type of control queues. This is an all + * or nothing routine; if one fails, all previously allocated queues will be + * destroyed. This must be called prior to using the individual add/remove + * APIs. + */ +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q, + struct iecm_ctlq_create_info *q_info) +{ + struct iecm_ctlq_info *cq = NULL, *tmp = NULL; + int ret_code = 0; + int i = 0; + + INIT_LIST_HEAD(&hw->cq_list_head); + + for (i = 0; i < num_q; i++) { + struct iecm_ctlq_create_info *qinfo = q_info + i; + + ret_code = iecm_ctlq_add(hw, qinfo, &cq); + if (ret_code) + goto init_destroy_qs; + } + + return ret_code; + +init_destroy_qs: + list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) + iecm_ctlq_remove(hw, cq); + + return ret_code; +} + +/** + * iecm_ctlq_deinit - destroy all control queues + * @hw: pointer to hw struct + */ +int iecm_ctlq_deinit(struct iecm_hw *hw) +{ + struct iecm_ctlq_info *cq = NULL, *tmp = NULL; + int ret_code = 0; + + list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) + iecm_ctlq_remove(hw, cq); + + return ret_code; +} + +/** + * iecm_ctlq_send - send command to Control Queue (CTQ) + * @hw: pointer to hw struct + * @cq: handle to control queue struct to send on + * @num_q_msg: number of messages to send on control queue + * @q_msg: pointer to array of queue messages to be sent + * + * The caller is expected to allocate DMAable buffers and pass them to the + * send routine via the q_msg struct / control queue specific data struct. + * The control queue will hold a reference to each send message until + * the completion for that message has been cleaned. + */ +int iecm_ctlq_send(struct iecm_hw *hw, struct iecm_ctlq_info *cq, + u16 num_q_msg, struct iecm_ctlq_msg q_msg[]) +{ + struct iecm_ctlq_desc *desc; + int num_desc_avail = 0; + int status = 0; + int i = 0; + + if (!cq || !cq->ring_size) + return -ENOBUFS; + + mutex_lock(&cq->cq_lock); + + /* Ensure there are enough descriptors to send all messages */ + num_desc_avail = IECM_CTLQ_DESC_UNUSED(cq); + if (num_desc_avail == 0 || num_desc_avail < num_q_msg) { + status = -ENOSPC; + goto sq_send_command_out; + } + + for (i = 0; i < num_q_msg; i++) { + struct iecm_ctlq_msg *msg = &q_msg[i]; + u64 msg_cookie; + + desc = IECM_CTLQ_DESC(cq, cq->next_to_use); + + desc->opcode = cpu_to_le16(msg->opcode); + desc->pfid_vfid = cpu_to_le16(msg->func_id); + + msg_cookie = *(u64 *)&msg->cookie; + desc->cookie_high = + cpu_to_le32(upper_32_bits(msg_cookie)); + desc->cookie_low = + cpu_to_le32(lower_32_bits(msg_cookie)); + + if (msg->data_len) { + struct iecm_dma_mem *buff = msg->ctx.indirect.payload; + + desc->datalen = cpu_to_le16(msg->data_len); + desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_BUF); + desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_RD); + + /* Update the address values in the desc with the pa + * value for respective buffer + */ + desc->params.indirect.addr_high = + cpu_to_le32(upper_32_bits(buff->pa)); + desc->params.indirect.addr_low = + cpu_to_le32(lower_32_bits(buff->pa)); + + memcpy(&desc->params, msg->ctx.indirect.context, + IECM_INDIRECT_CTX_SIZE); + } else { + memcpy(&desc->params, msg->ctx.direct, + IECM_DIRECT_CTX_SIZE); + } + + /* Store buffer info */ + cq->bi.tx_msg[cq->next_to_use] = msg; + + (cq->next_to_use)++; + if (cq->next_to_use == cq->ring_size) + cq->next_to_use = 0; + } + + /* Force memory write to complete before letting hardware + * know that there are new descriptors to fetch. + */ + dma_wmb(); + + wr32(hw, cq->reg.tail, cq->next_to_use); + +sq_send_command_out: + mutex_unlock(&cq->cq_lock); + + return status; +} + +/** + * iecm_ctlq_clean_sq - reclaim send descriptors on HW write back for the + * requested queue + * @cq: pointer to the specific Control queue + * @clean_count: (input|output) number of descriptors to clean as input, and + * number of descriptors actually cleaned as output + * @msg_status: (output) pointer to msg pointer array to be populated; needs + * to be allocated by caller + * + * Returns an array of message pointers associated with the cleaned + * descriptors. The pointers are to the original ctlq_msgs sent on the cleaned + * descriptors. The status will be returned for each; any messages that failed + * to send will have a non-zero status. The caller is expected to free original + * ctlq_msgs and free or reuse the DMA buffers. + */ +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count, + struct iecm_ctlq_msg *msg_status[]) +{ + struct iecm_ctlq_desc *desc; + u16 i = 0, num_to_clean; + u16 ntc, desc_err; + int ret = 0; + + if (!cq || !cq->ring_size) + return -ENOBUFS; + + if (*clean_count == 0) + return 0; + if (*clean_count > cq->ring_size) + return -EBADR; + + mutex_lock(&cq->cq_lock); + + ntc = cq->next_to_clean; + + num_to_clean = *clean_count; + + for (i = 0; i < num_to_clean; i++) { + /* Fetch next descriptor and check if marked as done */ + desc = IECM_CTLQ_DESC(cq, ntc); + if (!(le16_to_cpu(desc->flags) & IECM_CTLQ_FLAG_DD)) + break; + + desc_err = le16_to_cpu(desc->ret_val); + if (desc_err) { + /* strip off FW internal code */ + desc_err &= 0xff; + } + + msg_status[i] = cq->bi.tx_msg[ntc]; + msg_status[i]->status = desc_err; + + cq->bi.tx_msg[ntc] = NULL; + + /* Zero out any stale data */ + memset(desc, 0, sizeof(*desc)); + + ntc++; + if (ntc == cq->ring_size) + ntc = 0; + } + + cq->next_to_clean = ntc; + + mutex_unlock(&cq->cq_lock); + + /* Return number of descriptors actually cleaned */ + *clean_count = i; + + return ret; +} + +/** + * iecm_ctlq_post_rx_buffs - post buffers to descriptor ring + * @hw: pointer to hw struct + * @cq: pointer to control queue handle + * @buff_count: (input|output) input is number of buffers caller is trying to + * return; output is number of buffers that were not posted + * @buffs: array of pointers to dma mem structs to be given to hardware + * + * Caller uses this function to return DMA buffers to the descriptor ring after + * consuming them; buff_count will be the number of buffers. + * + * Note: this function needs to be called after a receive call even + * if there are no DMA buffers to be returned, i.e. buff_count = 0, + * buffs = NULL to support direct commands + */ +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, struct iecm_ctlq_info *cq, + u16 *buff_count, struct iecm_dma_mem **buffs) +{ + struct iecm_ctlq_desc *desc; + u16 ntp = cq->next_to_post; + bool buffs_avail = false; + u16 tbp = ntp + 1; + int status = 0; + int i = 0; + + if (*buff_count > cq->ring_size) + return -EBADR; + + if (*buff_count > 0) + buffs_avail = true; + + mutex_lock(&cq->cq_lock); + + if (tbp >= cq->ring_size) + tbp = 0; + + if (tbp == cq->next_to_clean) + /* Nothing to do */ + goto post_buffs_out; + + /* Post buffers for as many as provided or up until the last one used */ + while (ntp != cq->next_to_clean) { + desc = IECM_CTLQ_DESC(cq, ntp); + + if (cq->bi.rx_buff[ntp]) + goto fill_desc; + if (!buffs_avail) { + /* If the caller hasn't given us any buffers or + * there are none left, search the ring itself + * for an available buffer to move to this + * entry starting at the next entry in the ring + */ + tbp = ntp + 1; + + /* Wrap ring if necessary */ + if (tbp >= cq->ring_size) + tbp = 0; + + while (tbp != cq->next_to_clean) { + if (cq->bi.rx_buff[tbp]) { + cq->bi.rx_buff[ntp] = + cq->bi.rx_buff[tbp]; + cq->bi.rx_buff[tbp] = NULL; + + /* Found a buffer, no need to + * search anymore + */ + break; + } + + /* Wrap ring if necessary */ + tbp++; + if (tbp >= cq->ring_size) + tbp = 0; + } + + if (tbp == cq->next_to_clean) + goto post_buffs_out; + } else { + /* Give back pointer to DMA buffer */ + cq->bi.rx_buff[ntp] = buffs[i]; + i++; + + if (i >= *buff_count) + buffs_avail = false; + } + +fill_desc: + desc->flags = + cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD); + + /* Post buffers to descriptor */ + desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size); + desc->params.indirect.addr_high = + cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa)); + desc->params.indirect.addr_low = + cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa)); + + ntp++; + if (ntp == cq->ring_size) + ntp = 0; + } + +post_buffs_out: + /* Only update tail if buffers were actually posted */ + if (cq->next_to_post != ntp) { + if (ntp) + /* Update next_to_post to ntp - 1 since current ntp + * will not have a buffer + */ + cq->next_to_post = ntp - 1; + else + /* Wrap to end of end ring since current ntp is 0 */ + cq->next_to_post = cq->ring_size - 1; + + wr32(hw, cq->reg.tail, cq->next_to_post); + } + + mutex_unlock(&cq->cq_lock); + + /* return the number of buffers that were not posted */ + *buff_count = *buff_count - i; + + return status; +} + +/** + * iecm_ctlq_recv - receive control queue message call back + * @cq: pointer to control queue handle to receive on + * @num_q_msg: (input|output) input number of messages that should be received; + * output number of messages actually received + * @q_msg: (output) array of received control queue messages on this q; + * needs to be pre-allocated by caller for as many messages as requested + * + * Called by interrupt handler or polling mechanism. Caller is expected + * to free buffers + */ +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg, + struct iecm_ctlq_msg *q_msg) +{ + u16 num_to_clean, ntc, ret_val, flags; + struct iecm_ctlq_desc *desc; + int ret_code = 0; + u16 i = 0; + + if (!cq || !cq->ring_size) + return -ENOBUFS; + + if (*num_q_msg == 0) + return 0; + else if (*num_q_msg > cq->ring_size) + return -EBADR; + + /* take the lock before we start messing with the ring */ + mutex_lock(&cq->cq_lock); + + ntc = cq->next_to_clean; + + num_to_clean = *num_q_msg; + + for (i = 0; i < num_to_clean; i++) { + u64 msg_cookie; + + /* Fetch next descriptor and check if marked as done */ + desc = IECM_CTLQ_DESC(cq, ntc); + flags = le16_to_cpu(desc->flags); + + if (!(flags & IECM_CTLQ_FLAG_DD)) + break; + + ret_val = le16_to_cpu(desc->ret_val); + + q_msg[i].vmvf_type = (flags & + (IECM_CTLQ_FLAG_FTYPE_VM | + IECM_CTLQ_FLAG_FTYPE_PF)) >> + IECM_CTLQ_FLAG_FTYPE_S; + + if (flags & IECM_CTLQ_FLAG_ERR) + ret_code = -EBADMSG; + + msg_cookie = (u64)le32_to_cpu(desc->cookie_high) << 32; + msg_cookie |= (u64)le32_to_cpu(desc->cookie_low); + memcpy(&q_msg[i].cookie, &msg_cookie, sizeof(u64)); + + q_msg[i].opcode = le16_to_cpu(desc->opcode); + q_msg[i].data_len = le16_to_cpu(desc->datalen); + q_msg[i].status = ret_val; + + if (desc->datalen) { + memcpy(q_msg[i].ctx.indirect.context, + &desc->params.indirect, IECM_INDIRECT_CTX_SIZE); + + /* Assign pointer to dma buffer to ctlq_msg array + * to be given to upper layer + */ + q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc]; + + /* Zero out pointer to DMA buffer info; + * will be repopulated by post buffers API + */ + cq->bi.rx_buff[ntc] = NULL; + } else { + memcpy(q_msg[i].ctx.direct, desc->params.raw, + IECM_DIRECT_CTX_SIZE); + } + + /* Zero out stale data in descriptor */ + memset(desc, 0, sizeof(struct iecm_ctlq_desc)); + + ntc++; + if (ntc == cq->ring_size) + ntc = 0; + }; + + cq->next_to_clean = ntc; + + mutex_unlock(&cq->cq_lock); + + *num_q_msg = i; + if (*num_q_msg == 0) + ret_code = -ENOMSG; + + return ret_code; +} diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c new file mode 100644 index 000000000000..a36fc88d6bb5 --- /dev/null +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, Intel Corporation. */ + +#include "iecm_controlq.h" + +/** + * iecm_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + */ +static int +iecm_ctlq_alloc_desc_ring(struct iecm_hw *hw, + struct iecm_ctlq_info *cq) +{ + size_t size = cq->ring_size * sizeof(struct iecm_ctlq_desc); + + cq->desc_ring.va = iecm_alloc_dma_mem(hw, &cq->desc_ring, size); + if (!cq->desc_ring.va) + return -ENOMEM; + + return 0; +} + +/** + * iecm_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * + * Allocate the buffer head for all control queues, and if it's a receive + * queue, allocate DMA buffers + */ +static int iecm_ctlq_alloc_bufs(struct iecm_hw *hw, + struct iecm_ctlq_info *cq) +{ + int i = 0; + + /* Do not allocate DMA buffers for transmit queues */ + if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_TX) + return 0; + + /* We'll be allocating the buffer info memory first, then we can + * allocate the mapped buffers for the event processing + */ + cq->bi.rx_buff = kcalloc(cq->ring_size, sizeof(struct iecm_dma_mem *), + GFP_KERNEL); + if (!cq->bi.rx_buff) + return -ENOMEM; + + /* allocate the mapped buffers (except for the last one) */ + for (i = 0; i < cq->ring_size - 1; i++) { + struct iecm_dma_mem *bi; + int num = 1; /* number of iecm_dma_mem to be allocated */ + + cq->bi.rx_buff[i] = kcalloc(num, sizeof(struct iecm_dma_mem), + GFP_KERNEL); + if (!cq->bi.rx_buff[i]) + goto unwind_alloc_cq_bufs; + + bi = cq->bi.rx_buff[i]; + + bi->va = iecm_alloc_dma_mem(hw, bi, cq->buf_size); + if (!bi->va) { + /* unwind will not free the failed entry */ + kfree(cq->bi.rx_buff[i]); + goto unwind_alloc_cq_bufs; + } + } + + return 0; + +unwind_alloc_cq_bufs: + /* don't try to free the one that failed... */ + i--; + for (; i >= 0; i--) { + iecm_free_dma_mem(hw, cq->bi.rx_buff[i]); + kfree(cq->bi.rx_buff[i]); + } + kfree(cq->bi.rx_buff); + + return -ENOMEM; +} + +/** + * iecm_ctlq_free_desc_ring - Free Control Queue (CQ) rings + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * + * This assumes the posted send buffers have already been cleaned + * and de-allocated + */ +static void iecm_ctlq_free_desc_ring(struct iecm_hw *hw, + struct iecm_ctlq_info *cq) +{ + iecm_free_dma_mem(hw, &cq->desc_ring); +} + +/** + * iecm_ctlq_free_bufs - Free CQ buffer info elements + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * + * Free the DMA buffers for RX queues, and DMA buffer header for both RX and TX + * queues. The upper layers are expected to manage freeing of TX DMA buffers + */ +static void iecm_ctlq_free_bufs(struct iecm_hw *hw, struct iecm_ctlq_info *cq) +{ + void *bi; + + if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_RX) { + int i; + + /* free DMA buffers for rx queues*/ + for (i = 0; i < cq->ring_size; i++) { + if (cq->bi.rx_buff[i]) { + iecm_free_dma_mem(hw, cq->bi.rx_buff[i]); + kfree(cq->bi.rx_buff[i]); + } + } + + bi = (void *)cq->bi.rx_buff; + } else { + bi = (void *)cq->bi.tx_msg; + } + + /* free the buffer header */ + kfree(bi); +} + +/** + * iecm_ctlq_dealloc_ring_res - Free memory allocated for control queue + * @hw: pointer to hw struct + * @cq: pointer to the specific Control queue + * + * Free the memory used by the ring, buffers and other related structures + */ +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq) +{ + /* free ring buffers and the ring itself */ + iecm_ctlq_free_bufs(hw, cq); + iecm_ctlq_free_desc_ring(hw, cq); +} + +/** + * iecm_ctlq_alloc_ring_res - allocate memory for descriptor ring and bufs + * @hw: pointer to hw struct + * @cq: pointer to control queue struct + * + * Do *NOT* hold the lock when calling this as the memory allocation routines + * called are not going to be atomic context safe + */ +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq) +{ + int ret_code; + + /* verify input for valid configuration */ + if (!cq->ring_size || !cq->buf_size) + return -EINVAL; + + /* allocate the ring memory */ + ret_code = iecm_ctlq_alloc_desc_ring(hw, cq); + if (ret_code) + return ret_code; + + /* allocate buffers in the rings */ + ret_code = iecm_ctlq_alloc_bufs(hw, cq); + if (ret_code) + goto iecm_init_cq_free_ring; + + /* success! */ + return 0; + +iecm_init_cq_free_ring: + iecm_free_dma_mem(hw, &cq->desc_ring); + return ret_code; +} diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c index e6d0b418a27f..64cdbce2c842 100644 --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c @@ -5,6 +5,25 @@ #include "iecm.h" +/** + * iecm_cfg_hw - Initialize HW struct + * @adapter: adapter to setup hw struct for + * + * Returns 0 on success, negative on failure + */ +static int iecm_cfg_hw(struct iecm_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + struct iecm_hw *hw = &adapter->hw; + + hw->hw_addr = pcim_iomap_table(pdev)[IECM_BAR0]; + if (!hw->hw_addr) + return -EIO; + hw->back = adapter; + + return 0; +} + /** * iecm_statistics_task - Delayed task to get statistics over mailbox * @work: work_struct handle to our data @@ -39,6 +58,32 @@ static void iecm_init_task(struct work_struct *work) /* stub */ } +/** + * iecm_api_init - Initialize and verify device API + * @adapter: driver specific private structure + * + * Returns 0 on success, negative on failure + */ +static int iecm_api_init(struct iecm_adapter *adapter) +{ + struct iecm_reg_ops *reg_ops = &adapter->dev_ops.reg_ops; + struct pci_dev *pdev = adapter->pdev; + + if (!adapter->dev_ops.reg_ops_init) { + dev_err(&pdev->dev, "Invalid device, register API init not defined\n"); + return -EINVAL; + } + adapter->dev_ops.reg_ops_init(adapter); + if (!(reg_ops->ctlq_reg_init && reg_ops->intr_reg_init && + reg_ops->mb_intr_reg_init && reg_ops->reset_reg_init && + reg_ops->trigger_reset)) { + dev_err(&pdev->dev, "Invalid device, missing one or more register functions\n"); + return -EINVAL; + } + + return 0; +} + /** * iecm_deinit_task - Device deinit routine * @adapter: Driver specific private structue @@ -51,13 +96,108 @@ static void iecm_deinit_task(struct iecm_adapter *adapter) /* stub */ } +/** + * iecm_check_reset_complete - check that reset is complete + * @hw: pointer to hw struct + * @reset_reg: struct with reset registers + * + * Returns 0 if device is ready to use, or -EBUSY if it's in reset. + **/ +static int iecm_check_reset_complete(struct iecm_hw *hw, + struct iecm_reset_reg *reset_reg) +{ + struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back; + int i; + + for (i = 0; i < 2000; i++) { + u32 reg_val = rd32(hw, reset_reg->rstat); + + /* 0xFFFFFFFF might be read if other side hasn't cleared the + * register for us yet and 0xFFFFFFFF is not a valid value for + * the register, so treat that as invalid. + */ + if (reg_val != 0xFFFFFFFF && (reg_val & reset_reg->rstat_m)) + return 0; + usleep_range(5000, 10000); + } + + dev_warn(&adapter->pdev->dev, "Device reset timeout!\n"); + return -EBUSY; +} + +/** + * iecm_init_hard_reset - Initiate a hardware reset + * @adapter: Driver specific private structure + * + * Deallocate the vports and all the resources associated with them and + * reallocate. Also reinitialize the mailbox. Return 0 on success, + * negative on failure. + */ +static int iecm_init_hard_reset(struct iecm_adapter *adapter) +{ + int err = 0; + + mutex_lock(&adapter->reset_lock); + + /* Prepare for reset */ + if (test_and_clear_bit(__IECM_HR_DRV_LOAD, adapter->flags)) { + adapter->dev_ops.reg_ops.trigger_reset(adapter, + __IECM_HR_DRV_LOAD); + } else if (test_and_clear_bit(__IECM_HR_FUNC_RESET, adapter->flags)) { + bool is_reset = iecm_is_reset_detected(adapter); + + if (adapter->state == __IECM_UP) + set_bit(__IECM_UP_REQUESTED, adapter->flags); + iecm_deinit_task(adapter); + if (!is_reset) + adapter->dev_ops.reg_ops.trigger_reset(adapter, + __IECM_HR_FUNC_RESET); + iecm_deinit_dflt_mbx(adapter); + } else if (test_and_clear_bit(__IECM_HR_CORE_RESET, adapter->flags)) { + if (adapter->state == __IECM_UP) + set_bit(__IECM_UP_REQUESTED, adapter->flags); + iecm_deinit_task(adapter); + } else { + dev_err(&adapter->pdev->dev, "Unhandled hard reset cause\n"); + err = -EBADRQC; + goto handle_err; + } + + /* Wait for reset to complete */ + err = iecm_check_reset_complete(&adapter->hw, &adapter->reset_reg); + if (err) { + dev_err(&adapter->pdev->dev, "The driver was unable to contact the device's firmware. Check that the FW is running. Driver state=%u\n", + adapter->state); + goto handle_err; + } + + /* Reset is complete and so start building the driver resources again */ + err = iecm_init_dflt_mbx(adapter); + if (err) { + dev_err(&adapter->pdev->dev, "Failed to initialize default mailbox: %d\n", + err); + } +handle_err: + mutex_unlock(&adapter->reset_lock); + return err; +} + /** * iecm_vc_event_task - Handle virtchannel event logic * @work: work queue struct */ static void iecm_vc_event_task(struct work_struct *work) { - /* stub */ + struct iecm_adapter *adapter = container_of(work, + struct iecm_adapter, + vc_event_task.work); + + if (test_bit(__IECM_HR_CORE_RESET, adapter->flags) || + test_bit(__IECM_HR_FUNC_RESET, adapter->flags) || + test_bit(__IECM_HR_DRV_LOAD, adapter->flags)) { + set_bit(__IECM_HR_RESET_IN_PROG, adapter->flags); + iecm_init_hard_reset(adapter); + } } /** @@ -75,6 +215,11 @@ int iecm_probe(struct pci_dev *pdev, int err; adapter->pdev = pdev; + err = iecm_api_init(adapter); + if (err) { + dev_err(&pdev->dev, "Device API is incorrectly configured\n"); + return err; + } err = pcim_enable_device(pdev); if (err) @@ -147,6 +292,20 @@ int iecm_probe(struct pci_dev *pdev, goto err_netdev_alloc; } + err = iecm_vport_params_buf_alloc(adapter); + if (err) { + dev_err(&pdev->dev, "Failed to alloc vport params buffer: %d\n", + err); + goto err_mb_res; + } + + err = iecm_cfg_hw(adapter); + if (err) { + dev_err(&pdev->dev, "Failed to configure HW structure for adapter: %d\n", + err); + goto err_cfg_hw; + } + mutex_init(&adapter->sw_mutex); mutex_init(&adapter->reset_lock); init_waitqueue_head(&adapter->vchnl_wq); @@ -166,11 +325,16 @@ int iecm_probe(struct pci_dev *pdev, INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task); INIT_DELAYED_WORK(&adapter->vc_event_task, iecm_vc_event_task); + adapter->dev_ops.reg_ops.reset_reg_init(&adapter->reset_reg); set_bit(__IECM_HR_DRV_LOAD, adapter->flags); queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task, msecs_to_jiffies(10 * (pdev->devfn & 0x07))); return 0; +err_cfg_hw: + iecm_vport_params_buf_rel(adapter); +err_mb_res: + kfree(adapter->netdevs); err_netdev_alloc: kfree(adapter->vports); err_vport_alloc: @@ -214,6 +378,7 @@ void iecm_remove(struct pci_dev *pdev) cancel_delayed_work_sync(&adapter->vc_event_task); iecm_deinit_task(adapter); iecm_del_user_cfg_data(adapter); + iecm_deinit_dflt_mbx(adapter); msleep(20); destroy_workqueue(adapter->serv_wq); destroy_workqueue(adapter->vc_event_wq); @@ -222,6 +387,7 @@ void iecm_remove(struct pci_dev *pdev) kfree(adapter->vports); kfree(adapter->netdevs); kfree(adapter->vlan_caps); + iecm_vport_params_buf_rel(adapter); mutex_destroy(&adapter->sw_mutex); mutex_destroy(&adapter->reset_lock); pci_disable_pcie_error_reporting(pdev); @@ -229,3 +395,26 @@ void iecm_remove(struct pci_dev *pdev) pci_disable_device(pdev); } EXPORT_SYMBOL(iecm_remove); + +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size) +{ + struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back; + size_t sz = ALIGN(size, 4096); + + mem->va = dma_alloc_coherent(&adapter->pdev->dev, sz, + &mem->pa, GFP_KERNEL | __GFP_ZERO); + mem->size = size; + + return mem->va; +} + +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem) +{ + struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back; + + dma_free_coherent(&adapter->pdev->dev, mem->size, + mem->va, mem->pa); + mem->size = 0; + mem->va = NULL; + mem->pa = 0; +} diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c new file mode 100644 index 000000000000..b8f54b8c700a --- /dev/null +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2019 Intel Corporation */ + +#include "iecm.h" + +/** + * iecm_mb_clean - Reclaim the send mailbox queue entries + * @adapter: Driver specific private structure + * + * Reclaim the send mailbox queue entries to be used to send further messages + * + * Returns 0 on success, negative on failure + */ +static int iecm_mb_clean(struct iecm_adapter *adapter) +{ + u16 i, num_q_msg = IECM_DFLT_MBX_Q_LEN; + struct iecm_ctlq_msg **q_msg; + struct iecm_dma_mem *dma_mem; + int err = 0; + + q_msg = kcalloc(num_q_msg, sizeof(struct iecm_ctlq_msg *), GFP_KERNEL); + if (!q_msg) + return -ENOMEM; + + err = iecm_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg); + if (err) + goto error; + + for (i = 0; i < num_q_msg; i++) { + dma_mem = q_msg[i]->ctx.indirect.payload; + if (dma_mem) + dmam_free_coherent(&adapter->pdev->dev, dma_mem->size, + dma_mem->va, dma_mem->pa); + kfree(q_msg[i]); + kfree(dma_mem); + } +error: + kfree(q_msg); + return err; +} + +/** + * iecm_find_ctlq - Given a type and id, find ctlq info + * @hw: hardware struct + * @type: type of ctrlq to find + * @id: ctlq id to find + * + * Returns pointer to found ctlq info struct, NULL otherwise. + */ +static struct iecm_ctlq_info *iecm_find_ctlq(struct iecm_hw *hw, + enum iecm_ctlq_type type, int id) +{ + struct iecm_ctlq_info *cq, *tmp; + + list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) { + if (cq->q_id == id && cq->cq_type == type) + return cq; + } + + return NULL; +} + +/** + * iecm_init_dflt_mbx - Setup default mailbox parameters and make request + * @adapter: adapter info struct + * + * Returns 0 on success, negative otherwise + */ +int iecm_init_dflt_mbx(struct iecm_adapter *adapter) +{ + struct iecm_ctlq_create_info ctlq_info[] = { + { + .type = IECM_CTLQ_TYPE_MAILBOX_TX, + .id = IECM_DFLT_MBX_ID, + .len = IECM_DFLT_MBX_Q_LEN, + .buf_size = IECM_DFLT_MBX_BUF_SIZE + }, + { + .type = IECM_CTLQ_TYPE_MAILBOX_RX, + .id = IECM_DFLT_MBX_ID, + .len = IECM_DFLT_MBX_Q_LEN, + .buf_size = IECM_DFLT_MBX_BUF_SIZE + } + }; + struct iecm_hw *hw = &adapter->hw; + int err; + + adapter->dev_ops.reg_ops.ctlq_reg_init(ctlq_info); + +#define NUM_Q 2 + err = iecm_ctlq_init(hw, NUM_Q, ctlq_info); + if (err) + return err; + + hw->asq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_TX, + IECM_DFLT_MBX_ID); + hw->arq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_RX, + IECM_DFLT_MBX_ID); + + if (!hw->asq || !hw->arq) { + iecm_ctlq_deinit(hw); + return -ENOENT; + } + adapter->state = __IECM_STARTUP; + /* Skew the delay for init tasks for each function based on fn number + * to prevent every function from making the same call simulatenously. + */ + queue_delayed_work(adapter->init_wq, &adapter->init_task, + msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07))); + return 0; +} + +/** + * iecm_deinit_dflt_mbx - Free up ctlqs setup + * @adapter: Driver specific private data structure + */ +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter) +{ + if (adapter->hw.arq && adapter->hw.asq) { + iecm_mb_clean(adapter); + iecm_ctlq_deinit(&adapter->hw); + } + adapter->hw.arq = NULL; + adapter->hw.asq = NULL; +} + +/** + * iecm_vport_params_buf_alloc - Allocate memory for MailBox resources + * @adapter: Driver specific private data structure + * + * Will alloc memory to hold the vport parameters received on MailBox + */ +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter) +{ + adapter->vport_params_reqd = kcalloc(IECM_MAX_NUM_VPORTS, + sizeof(*adapter->vport_params_reqd), + GFP_KERNEL); + if (!adapter->vport_params_reqd) + return -ENOMEM; + + adapter->vport_params_recvd = kcalloc(IECM_MAX_NUM_VPORTS, + sizeof(*adapter->vport_params_recvd), + GFP_KERNEL); + if (!adapter->vport_params_recvd) { + kfree(adapter->vport_params_reqd); + return -ENOMEM; + } + + return 0; +} + +/** + * iecm_vport_params_buf_rel - Release memory for MailBox resources + * @adapter: Driver specific private data structure + * + * Will release memory to hold the vport parameters received on MailBox + */ +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter) +{ + int i = 0; + + for (i = 0; i < IECM_MAX_NUM_VPORTS; i++) { + kfree(adapter->vport_params_recvd[i]); + kfree(adapter->vport_params_reqd[i]); + } + + kfree(adapter->vport_params_recvd); + kfree(adapter->vport_params_reqd); + + kfree(adapter->caps); + kfree(adapter->config_data.req_qs_chunks); +} diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h index e19e014e9817..ca9029224e06 100644 --- a/drivers/net/ethernet/intel/include/iecm.h +++ b/drivers/net/ethernet/intel/include/iecm.h @@ -12,15 +12,33 @@ #include #include "iecm_txrx.h" +#include "iecm_controlq.h" #define IECM_BAR0 0 #define IECM_NO_FREE_SLOT 0xffff +/* Default Mailbox settings */ +#define IECM_DFLT_MBX_BUF_SIZE (4 * 1024) +#define IECM_NUM_QCTX_PER_MSG 3 +#define IECM_NUM_FILTERS_PER_MSG 20 +#define IECM_VLANS_PER_MSG \ + ((IECM_DFLT_MBX_BUF_SIZE - sizeof(struct virtchnl_vlan_filter_list)) \ + / sizeof(u16)) +#define IECM_DFLT_MBX_Q_LEN 64 +#define IECM_DFLT_MBX_ID -1 +/* maximum number of times to try before resetting mailbox */ +#define IECM_MB_MAX_ERR 20 +#define IECM_NUM_CHUNKS_PER_MSG(a, b) ((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b)) + #define IECM_MAX_NUM_VPORTS 1 /* available message levels */ #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +/* Forward declaration */ +struct iecm_adapter; +struct iecm_vport; + enum iecm_state { __IECM_STARTUP, __IECM_VER_CHECK, @@ -77,6 +95,22 @@ struct iecm_reset_reg { u32 rstat_m; }; +/* product specific register API */ +struct iecm_reg_ops { + void (*ctlq_reg_init)(struct iecm_ctlq_create_info *cq); + int (*intr_reg_init)(struct iecm_vport *vport); + void (*mb_intr_reg_init)(struct iecm_adapter *adapter); + void (*reset_reg_init)(struct iecm_reset_reg *reset_reg); + void (*trigger_reset)(struct iecm_adapter *adapter, + enum iecm_flags trig_cause); +}; + +struct iecm_dev_ops { + void (*reg_ops_init)(struct iecm_adapter *adapter); + void (*crc_enable)(u64 *td_cmd); + struct iecm_reg_ops reg_ops; +}; + /* stub */ struct iecm_vport { }; @@ -124,6 +158,7 @@ struct iecm_adapter { DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS); struct mutex reset_lock; /* lock to protect reset flows */ struct iecm_reset_reg reset_reg; + struct iecm_hw hw; u16 num_req_msix; u16 num_msix_entries; @@ -156,6 +191,7 @@ struct iecm_adapter { wait_queue_head_t vchnl_wq; wait_queue_head_t sw_marker_wq; struct iecm_rss_data rss_data; + struct iecm_dev_ops dev_ops; s32 link_speed; /* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set * in vf_res->vf_cap_flags. This field should be used going forward and @@ -179,8 +215,24 @@ struct iecm_adapter { spinlock_t fdir_fltr_list_lock; }; +/** + * iecm_is_reset_detected - check if we were reset at some point + * @adapter: driver specific private structure + * + * Returns true if we are either in reset currently or were previously reset. + */ +static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter) +{ + return !(rd32(&adapter->hw, adapter->hw.arq->reg.len) & + adapter->hw.arq->reg.len_ena_mask); +} + int iecm_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent, struct iecm_adapter *adapter); void iecm_remove(struct pci_dev *pdev); +int iecm_init_dflt_mbx(struct iecm_adapter *adapter); +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter); +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter); +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter); #endif /* !_IECM_H_ */ diff --git a/drivers/net/ethernet/intel/include/iecm_controlq.h b/drivers/net/ethernet/intel/include/iecm_controlq.h new file mode 100644 index 000000000000..f2539baa2ce1 --- /dev/null +++ b/drivers/net/ethernet/intel/include/iecm_controlq.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, Intel Corporation. */ + +#ifndef _IECM_CONTROLQ_H_ +#define _IECM_CONTROLQ_H_ + +#include + +#include "iecm_controlq_api.h" + +/* Maximum buffer lengths for all control queue types */ +#define IECM_CTLQ_MAX_RING_SIZE 1024 +#define IECM_CTLQ_MAX_BUF_LEN 4096 + +#define IECM_CTLQ_DESC(R, i) \ + (&(((struct iecm_ctlq_desc *)((R)->desc_ring.va))[i])) + +#define IECM_CTLQ_DESC_UNUSED(R) \ + ((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size) + \ + (R)->next_to_clean - (R)->next_to_use - 1)) + +/* Control Queue default settings */ +#define IECM_CTRL_SQ_CMD_TIMEOUT 250 /* msecs */ + +struct iecm_ctlq_desc { + __le16 flags; + __le16 opcode; + __le16 datalen; /* 0 for direct commands */ + union { + __le16 ret_val; + __le16 pfid_vfid; +#define IECM_CTLQ_DESC_VF_ID_S 0 +#define IECM_CTLQ_DESC_VF_ID_M (0x7FF << IECM_CTLQ_DESC_VF_ID_S) +#define IECM_CTLQ_DESC_PF_ID_S 11 +#define IECM_CTLQ_DESC_PF_ID_M (0x1F << IECM_CTLQ_DESC_PF_ID_S) + }; + __le32 cookie_high; + __le32 cookie_low; + union { + struct { + __le32 param0; + __le32 param1; + __le32 param2; + __le32 param3; + } direct; + struct { + __le32 param0; + __le32 param1; + __le32 addr_high; + __le32 addr_low; + } indirect; + u8 raw[16]; + } params; +}; + +/* Flags sub-structure + * |0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10 |11 |12 |13 |14 |15 | + * |DD |CMP|ERR| * RSV * |FTYPE | *RSV* |RD |VFC|BUF| * RSV * | + */ +/* command flags and offsets */ +#define IECM_CTLQ_FLAG_DD_S 0 +#define IECM_CTLQ_FLAG_CMP_S 1 +#define IECM_CTLQ_FLAG_ERR_S 2 +#define IECM_CTLQ_FLAG_FTYPE_S 6 +#define IECM_CTLQ_FLAG_RD_S 10 +#define IECM_CTLQ_FLAG_VFC_S 11 +#define IECM_CTLQ_FLAG_BUF_S 12 + +#define IECM_CTLQ_FLAG_DD BIT(IECM_CTLQ_FLAG_DD_S) /* 0x1 */ +#define IECM_CTLQ_FLAG_CMP BIT(IECM_CTLQ_FLAG_CMP_S) /* 0x2 */ +#define IECM_CTLQ_FLAG_ERR BIT(IECM_CTLQ_FLAG_ERR_S) /* 0x4 */ +#define IECM_CTLQ_FLAG_FTYPE_VM BIT(IECM_CTLQ_FLAG_FTYPE_S) /* 0x40 */ +#define IECM_CTLQ_FLAG_FTYPE_PF BIT(IECM_CTLQ_FLAG_FTYPE_S + 1) /* 0x80 */ +#define IECM_CTLQ_FLAG_RD BIT(IECM_CTLQ_FLAG_RD_S) /* 0x400 */ +#define IECM_CTLQ_FLAG_VFC BIT(IECM_CTLQ_FLAG_VFC_S) /* 0x800 */ +#define IECM_CTLQ_FLAG_BUF BIT(IECM_CTLQ_FLAG_BUF_S) /* 0x1000 */ + +struct iecm_mbxq_desc { + u8 pad[8]; /* CTLQ flags/opcode/len/retval fields */ + u32 chnl_opcode; /* avoid confusion with desc->opcode */ + u32 chnl_retval; /* ditto for desc->retval */ + u32 pf_vf_id; /* used by CP when sending to PF */ +}; + +/* Define the APF hardware struct to replace other control structs as needed + * Align to ctlq_hw_info + */ +struct iecm_hw { + u8 __iomem *hw_addr; + u64 hw_addr_len; + void *back; + + /* control queue - send and receive */ + struct iecm_ctlq_info *asq; + struct iecm_ctlq_info *arq; + + /* pci info */ + u16 device_id; + u16 vendor_id; + u16 subsystem_device_id; + u16 subsystem_vendor_id; + u8 revision_id; + bool adapter_stopped; + + struct list_head cq_list_head; +}; + +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, + struct iecm_ctlq_info *cq); + +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq); + +/* prototype for functions used for dynamic memory allocation */ +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, + u64 size); +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem); +#endif /* _IECM_CONTROLQ_H_ */ diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h b/drivers/net/ethernet/intel/include/iecm_controlq_api.h new file mode 100644 index 000000000000..5f624f005d33 --- /dev/null +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, Intel Corporation. */ + +#ifndef _IECM_CONTROLQ_API_H_ +#define _IECM_CONTROLQ_API_H_ + +#include "iecm_mem.h" + +struct iecm_hw; + +/* Used for queue init, response and events */ +enum iecm_ctlq_type { + IECM_CTLQ_TYPE_MAILBOX_TX = 0, + IECM_CTLQ_TYPE_MAILBOX_RX = 1, + IECM_CTLQ_TYPE_CONFIG_TX = 2, + IECM_CTLQ_TYPE_CONFIG_RX = 3, + IECM_CTLQ_TYPE_EVENT_RX = 4, + IECM_CTLQ_TYPE_RDMA_TX = 5, + IECM_CTLQ_TYPE_RDMA_RX = 6, + IECM_CTLQ_TYPE_RDMA_COMPL = 7 +}; + +/* Generic Control Queue Structures */ +struct iecm_ctlq_reg { + /* used for queue tracking */ + u32 head; + u32 tail; + /* Below applies only to default mb (if present) */ + u32 len; + u32 bah; + u32 bal; + u32 len_mask; + u32 len_ena_mask; + u32 head_mask; +}; + +/* Generic queue msg structure */ +struct iecm_ctlq_msg { + u16 vmvf_type; /* represents the source of the message on recv */ +#define IECM_VMVF_TYPE_VF 0 +#define IECM_VMVF_TYPE_VM 1 +#define IECM_VMVF_TYPE_PF 2 + u16 opcode; + u16 data_len; /* data_len = 0 when no payload is attached */ + union { + u16 func_id; /* when sending a message */ + u16 status; /* when receiving a message */ + }; + union { + struct { + u32 chnl_retval; + u32 chnl_opcode; + } mbx; + } cookie; + union { +#define IECM_DIRECT_CTX_SIZE 16 +#define IECM_INDIRECT_CTX_SIZE 8 + /* 16 bytes of context can be provided or 8 bytes of context + * plus the address of a DMA buffer + */ + u8 direct[IECM_DIRECT_CTX_SIZE]; + struct { + u8 context[IECM_INDIRECT_CTX_SIZE]; + struct iecm_dma_mem *payload; + } indirect; + } ctx; +}; + +/* Generic queue info structures */ +/* MB, CONFIG and EVENT q do not have extended info */ +struct iecm_ctlq_create_info { + enum iecm_ctlq_type type; + int id; /* absolute queue offset passed as input + * -1 for default mailbox if present + */ + u16 len; /* Queue length passed as input */ + u16 buf_size; /* buffer size passed as input */ + u64 base_address; /* output, HPA of the Queue start */ + struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */ + + int ext_info_size; + void *ext_info; /* Specific to q type */ +}; + +/* Control Queue information */ +struct iecm_ctlq_info { + struct list_head cq_list; + + enum iecm_ctlq_type cq_type; + int q_id; + /* control queue lock */ + struct mutex cq_lock; + + /* used for interrupt processing */ + u16 next_to_use; + u16 next_to_clean; + u16 next_to_post; /* starting descriptor to post buffers + * to after recev + */ + + struct iecm_dma_mem desc_ring; /* descriptor ring memory + * iecm_dma_mem is defined in OSdep.h + */ + union { + struct iecm_dma_mem **rx_buff; + struct iecm_ctlq_msg **tx_msg; + } bi; + + u16 buf_size; /* queue buffer size */ + u16 ring_size; /* Number of descriptors */ + struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */ +}; + +/* PF/VF mailbox commands */ +enum iecm_mbx_opc { + /* iecm_mbq_opc_send_msg_to_pf: + * usage: used by PF or VF to send a message to its CPF + * target: RX queue and function ID of parent PF taken from HW + */ + iecm_mbq_opc_send_msg_to_pf = 0x0801, + + /* iecm_mbq_opc_send_msg_to_vf: + * usage: used by PF to send message to a VF + * target: VF control queue ID must be specified in descriptor + */ + iecm_mbq_opc_send_msg_to_vf = 0x0802, + + /* iecm_mbq_opc_send_msg_to_peer_pf: + * usage: used by any function to send message to any peer PF + * target: RX queue and host of parent PF taken from HW + */ + iecm_mbq_opc_send_msg_to_peer_pf = 0x0803, + + /* iecm_mbq_opc_send_msg_to_peer_drv: + * usage: used by any function to send message to any peer driver + * target: RX queue and target host must be specific in descriptor + */ + iecm_mbq_opc_send_msg_to_peer_drv = 0x0804, +}; + +/* API support for control queue management */ + +/* Will init all required q including default mb. "q_info" is an array of + * create_info structs equal to the number of control queues to be created. + */ +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q, + struct iecm_ctlq_create_info *q_info); + +/* Allocate and initialize a single control queue, which will be added to the + * control queue list; returns a handle to the created control queue + */ +int iecm_ctlq_add(struct iecm_hw *hw, + struct iecm_ctlq_create_info *qinfo, + struct iecm_ctlq_info **cq); + +/* Deinitialize and deallocate a single control queue */ +void iecm_ctlq_remove(struct iecm_hw *hw, + struct iecm_ctlq_info *cq); + +/* Sends messages to HW and will also free the buffer*/ +int iecm_ctlq_send(struct iecm_hw *hw, + struct iecm_ctlq_info *cq, + u16 num_q_msg, + struct iecm_ctlq_msg q_msg[]); + +/* Receives messages and called by interrupt handler/polling + * initiated by app/process. Also caller is supposed to free the buffers + */ +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg, + struct iecm_ctlq_msg *q_msg); + +/* Reclaims send descriptors on HW write back */ +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count, + struct iecm_ctlq_msg *msg_status[]); + +/* Indicate RX buffers are done being processed */ +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, + struct iecm_ctlq_info *cq, + u16 *buff_count, + struct iecm_dma_mem **buffs); + +/* Will destroy all q including the default mb */ +int iecm_ctlq_deinit(struct iecm_hw *hw); + +#endif /* _IECM_CONTROLQ_API_H_ */ diff --git a/drivers/net/ethernet/intel/include/iecm_mem.h b/drivers/net/ethernet/intel/include/iecm_mem.h new file mode 100644 index 000000000000..064dd6e10c24 --- /dev/null +++ b/drivers/net/ethernet/intel/include/iecm_mem.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2019 Intel Corporation */ + +#ifndef _IECM_MEM_H_ +#define _IECM_MEM_H_ + +#include + +struct iecm_dma_mem { + void *va; + dma_addr_t pa; + size_t size; +}; + +#define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg))) +#define rd32(a, reg) readl((a)->hw_addr + (reg)) +#define wr64(a, reg, value) writeq((value), ((a)->hw_addr + (reg))) +#define rd64(a, reg) readq((a)->hw_addr + (reg)) + +#endif /* _IECM_MEM_H_ */ -- 2.33.0