162306a36Sopenharmony_ci/********************************************************************** 262306a36Sopenharmony_ci * Author: Cavium, Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Contact: support@cavium.com 562306a36Sopenharmony_ci * Please include "LiquidIO" in the subject. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 1162306a36Sopenharmony_ci * published by the Free Software Foundation. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 1662306a36Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more details. 1762306a36Sopenharmony_ci ***********************************************************************/ 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/vmalloc.h> 2162306a36Sopenharmony_ci#include "liquidio_common.h" 2262306a36Sopenharmony_ci#include "octeon_droq.h" 2362306a36Sopenharmony_ci#include "octeon_iq.h" 2462306a36Sopenharmony_ci#include "response_manager.h" 2562306a36Sopenharmony_ci#include "octeon_device.h" 2662306a36Sopenharmony_ci#include "octeon_main.h" 2762306a36Sopenharmony_ci#include "octeon_network.h" 2862306a36Sopenharmony_ci#include "cn66xx_regs.h" 2962306a36Sopenharmony_ci#include "cn66xx_device.h" 3062306a36Sopenharmony_ci#include "cn23xx_pf_device.h" 3162306a36Sopenharmony_ci#include "cn23xx_vf_device.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct niclist { 3462306a36Sopenharmony_ci struct list_head list; 3562306a36Sopenharmony_ci void *ptr; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct __dispatch { 3962306a36Sopenharmony_ci struct list_head list; 4062306a36Sopenharmony_ci struct octeon_recv_info *rinfo; 4162306a36Sopenharmony_ci octeon_dispatch_fn_t disp_fn; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** Get the argument that the user set when registering dispatch 4562306a36Sopenharmony_ci * function for a given opcode/subcode. 4662306a36Sopenharmony_ci * @param octeon_dev - the octeon device pointer. 4762306a36Sopenharmony_ci * @param opcode - the opcode for which the dispatch argument 4862306a36Sopenharmony_ci * is to be checked. 4962306a36Sopenharmony_ci * @param subcode - the subcode for which the dispatch argument 5062306a36Sopenharmony_ci * is to be checked. 5162306a36Sopenharmony_ci * @return Success: void * (argument to the dispatch function) 5262306a36Sopenharmony_ci * @return Failure: NULL 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_civoid *octeon_get_dispatch_arg(struct octeon_device *octeon_dev, 5662306a36Sopenharmony_ci u16 opcode, u16 subcode) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci int idx; 5962306a36Sopenharmony_ci struct list_head *dispatch; 6062306a36Sopenharmony_ci void *fn_arg = NULL; 6162306a36Sopenharmony_ci u16 combined_opcode = OPCODE_SUBCODE(opcode, subcode); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci idx = combined_opcode & OCTEON_OPCODE_MASK; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spin_lock_bh(&octeon_dev->dispatch.lock); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (octeon_dev->dispatch.count == 0) { 6862306a36Sopenharmony_ci spin_unlock_bh(&octeon_dev->dispatch.lock); 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (octeon_dev->dispatch.dlist[idx].opcode == combined_opcode) { 7362306a36Sopenharmony_ci fn_arg = octeon_dev->dispatch.dlist[idx].arg; 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci list_for_each(dispatch, 7662306a36Sopenharmony_ci &octeon_dev->dispatch.dlist[idx].list) { 7762306a36Sopenharmony_ci if (((struct octeon_dispatch *)dispatch)->opcode == 7862306a36Sopenharmony_ci combined_opcode) { 7962306a36Sopenharmony_ci fn_arg = ((struct octeon_dispatch *) 8062306a36Sopenharmony_ci dispatch)->arg; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci spin_unlock_bh(&octeon_dev->dispatch.lock); 8762306a36Sopenharmony_ci return fn_arg; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** Check for packets on Droq. This function should be called with lock held. 9162306a36Sopenharmony_ci * @param droq - Droq on which count is checked. 9262306a36Sopenharmony_ci * @return Returns packet count. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ciu32 octeon_droq_check_hw_for_pkts(struct octeon_droq *droq) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci u32 pkt_count = 0; 9762306a36Sopenharmony_ci u32 last_count; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci pkt_count = readl(droq->pkts_sent_reg); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci last_count = pkt_count - droq->pkt_count; 10262306a36Sopenharmony_ci droq->pkt_count = pkt_count; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* we shall write to cnts at napi irq enable or end of droq tasklet */ 10562306a36Sopenharmony_ci if (last_count) 10662306a36Sopenharmony_ci atomic_add(last_count, &droq->pkts_pending); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return last_count; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_droq_check_hw_for_pkts); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void octeon_droq_compute_max_packet_bufs(struct octeon_droq *droq) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 count = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* max_empty_descs is the max. no. of descs that can have no buffers. 11762306a36Sopenharmony_ci * If the empty desc count goes beyond this value, we cannot safely 11862306a36Sopenharmony_ci * read in a 64K packet sent by Octeon 11962306a36Sopenharmony_ci * (64K is max pkt size from Octeon) 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci droq->max_empty_descs = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci do { 12462306a36Sopenharmony_ci droq->max_empty_descs++; 12562306a36Sopenharmony_ci count += droq->buffer_size; 12662306a36Sopenharmony_ci } while (count < (64 * 1024)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci droq->max_empty_descs = droq->max_count - droq->max_empty_descs; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void octeon_droq_reset_indices(struct octeon_droq *droq) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci droq->read_idx = 0; 13462306a36Sopenharmony_ci droq->write_idx = 0; 13562306a36Sopenharmony_ci droq->refill_idx = 0; 13662306a36Sopenharmony_ci droq->refill_count = 0; 13762306a36Sopenharmony_ci atomic_set(&droq->pkts_pending, 0); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void 14162306a36Sopenharmony_ciocteon_droq_destroy_ring_buffers(struct octeon_device *oct, 14262306a36Sopenharmony_ci struct octeon_droq *droq) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 i; 14562306a36Sopenharmony_ci struct octeon_skb_page_info *pg_info; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < droq->max_count; i++) { 14862306a36Sopenharmony_ci pg_info = &droq->recv_buf_list[i].pg_info; 14962306a36Sopenharmony_ci if (!pg_info) 15062306a36Sopenharmony_ci continue; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (pg_info->dma) 15362306a36Sopenharmony_ci lio_unmap_ring(oct->pci_dev, 15462306a36Sopenharmony_ci (u64)pg_info->dma); 15562306a36Sopenharmony_ci pg_info->dma = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (pg_info->page) 15862306a36Sopenharmony_ci recv_buffer_destroy(droq->recv_buf_list[i].buffer, 15962306a36Sopenharmony_ci pg_info); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci droq->recv_buf_list[i].buffer = NULL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci octeon_droq_reset_indices(droq); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int 16862306a36Sopenharmony_ciocteon_droq_setup_ring_buffers(struct octeon_device *oct, 16962306a36Sopenharmony_ci struct octeon_droq *droq) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci u32 i; 17262306a36Sopenharmony_ci void *buf; 17362306a36Sopenharmony_ci struct octeon_droq_desc *desc_ring = droq->desc_ring; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (i = 0; i < droq->max_count; i++) { 17662306a36Sopenharmony_ci buf = recv_buffer_alloc(oct, &droq->recv_buf_list[i].pg_info); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!buf) { 17962306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s buffer alloc failed\n", 18062306a36Sopenharmony_ci __func__); 18162306a36Sopenharmony_ci droq->stats.rx_alloc_failure++; 18262306a36Sopenharmony_ci return -ENOMEM; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci droq->recv_buf_list[i].buffer = buf; 18662306a36Sopenharmony_ci droq->recv_buf_list[i].data = get_rbd(buf); 18762306a36Sopenharmony_ci desc_ring[i].info_ptr = 0; 18862306a36Sopenharmony_ci desc_ring[i].buffer_ptr = 18962306a36Sopenharmony_ci lio_map_ring(droq->recv_buf_list[i].buffer); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci octeon_droq_reset_indices(droq); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci octeon_droq_compute_max_packet_bufs(droq); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint octeon_delete_droq(struct octeon_device *oct, u32 q_no) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct octeon_droq *droq = oct->droq[q_no]; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "%s[%d]\n", __func__, q_no); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci octeon_droq_destroy_ring_buffers(oct, droq); 20662306a36Sopenharmony_ci vfree(droq->recv_buf_list); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (droq->desc_ring) 20962306a36Sopenharmony_ci lio_dma_free(oct, (droq->max_count * OCT_DROQ_DESC_SIZE), 21062306a36Sopenharmony_ci droq->desc_ring, droq->desc_ring_dma); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci memset(droq, 0, OCT_DROQ_SIZE); 21362306a36Sopenharmony_ci oct->io_qmask.oq &= ~(1ULL << q_no); 21462306a36Sopenharmony_ci vfree(oct->droq[q_no]); 21562306a36Sopenharmony_ci oct->droq[q_no] = NULL; 21662306a36Sopenharmony_ci oct->num_oqs--; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_delete_droq); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciint octeon_init_droq(struct octeon_device *oct, 22362306a36Sopenharmony_ci u32 q_no, 22462306a36Sopenharmony_ci u32 num_descs, 22562306a36Sopenharmony_ci u32 desc_size, 22662306a36Sopenharmony_ci void *app_ctx) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct octeon_droq *droq; 22962306a36Sopenharmony_ci u32 desc_ring_size = 0, c_num_descs = 0, c_buf_size = 0; 23062306a36Sopenharmony_ci u32 c_pkts_per_intr = 0, c_refill_threshold = 0; 23162306a36Sopenharmony_ci int numa_node = dev_to_node(&oct->pci_dev->dev); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "%s[%d]\n", __func__, q_no); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci droq = oct->droq[q_no]; 23662306a36Sopenharmony_ci memset(droq, 0, OCT_DROQ_SIZE); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci droq->oct_dev = oct; 23962306a36Sopenharmony_ci droq->q_no = q_no; 24062306a36Sopenharmony_ci if (app_ctx) 24162306a36Sopenharmony_ci droq->app_ctx = app_ctx; 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci droq->app_ctx = (void *)(size_t)q_no; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci c_num_descs = num_descs; 24662306a36Sopenharmony_ci c_buf_size = desc_size; 24762306a36Sopenharmony_ci if (OCTEON_CN6XXX(oct)) { 24862306a36Sopenharmony_ci struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf6x); 25162306a36Sopenharmony_ci c_refill_threshold = 25262306a36Sopenharmony_ci (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf6x); 25362306a36Sopenharmony_ci } else if (OCTEON_CN23XX_PF(oct)) { 25462306a36Sopenharmony_ci struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23); 25762306a36Sopenharmony_ci c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23); 25862306a36Sopenharmony_ci } else if (OCTEON_CN23XX_VF(oct)) { 25962306a36Sopenharmony_ci struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_vf); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci c_pkts_per_intr = (u32)CFG_GET_OQ_PKTS_PER_INTR(conf23); 26262306a36Sopenharmony_ci c_refill_threshold = (u32)CFG_GET_OQ_REFILL_THRESHOLD(conf23); 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci return 1; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci droq->max_count = c_num_descs; 26862306a36Sopenharmony_ci droq->buffer_size = c_buf_size; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci desc_ring_size = droq->max_count * OCT_DROQ_DESC_SIZE; 27162306a36Sopenharmony_ci droq->desc_ring = lio_dma_alloc(oct, desc_ring_size, 27262306a36Sopenharmony_ci (dma_addr_t *)&droq->desc_ring_dma); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!droq->desc_ring) { 27562306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, 27662306a36Sopenharmony_ci "Output queue %d ring alloc failed\n", q_no); 27762306a36Sopenharmony_ci return 1; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "droq[%d]: desc_ring: virt: 0x%p, dma: %lx\n", 28162306a36Sopenharmony_ci q_no, droq->desc_ring, droq->desc_ring_dma); 28262306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "droq[%d]: num_desc: %d\n", q_no, 28362306a36Sopenharmony_ci droq->max_count); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci droq->recv_buf_list = vzalloc_node(array_size(droq->max_count, OCT_DROQ_RECVBUF_SIZE), 28662306a36Sopenharmony_ci numa_node); 28762306a36Sopenharmony_ci if (!droq->recv_buf_list) 28862306a36Sopenharmony_ci droq->recv_buf_list = vzalloc(array_size(droq->max_count, OCT_DROQ_RECVBUF_SIZE)); 28962306a36Sopenharmony_ci if (!droq->recv_buf_list) { 29062306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Output queue recv buf list alloc failed\n"); 29162306a36Sopenharmony_ci goto init_droq_fail; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (octeon_droq_setup_ring_buffers(oct, droq)) 29562306a36Sopenharmony_ci goto init_droq_fail; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci droq->pkts_per_intr = c_pkts_per_intr; 29862306a36Sopenharmony_ci droq->refill_threshold = c_refill_threshold; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "DROQ INIT: max_empty_descs: %d\n", 30162306a36Sopenharmony_ci droq->max_empty_descs); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci INIT_LIST_HEAD(&droq->dispatch_list); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* For 56xx Pass1, this function won't be called, so no checks. */ 30662306a36Sopenharmony_ci oct->fn_list.setup_oq_regs(oct, q_no); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci oct->io_qmask.oq |= BIT_ULL(q_no); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciinit_droq_fail: 31362306a36Sopenharmony_ci octeon_delete_droq(oct, q_no); 31462306a36Sopenharmony_ci return 1; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* octeon_create_recv_info 31862306a36Sopenharmony_ci * Parameters: 31962306a36Sopenharmony_ci * octeon_dev - pointer to the octeon device structure 32062306a36Sopenharmony_ci * droq - droq in which the packet arrived. 32162306a36Sopenharmony_ci * buf_cnt - no. of buffers used by the packet. 32262306a36Sopenharmony_ci * idx - index in the descriptor for the first buffer in the packet. 32362306a36Sopenharmony_ci * Description: 32462306a36Sopenharmony_ci * Allocates a recv_info_t and copies the buffer addresses for packet data 32562306a36Sopenharmony_ci * into the recv_pkt space which starts at an 8B offset from recv_info_t. 32662306a36Sopenharmony_ci * Flags the descriptors for refill later. If available descriptors go 32762306a36Sopenharmony_ci * below the threshold to receive a 64K pkt, new buffers are first allocated 32862306a36Sopenharmony_ci * before the recv_pkt_t is created. 32962306a36Sopenharmony_ci * This routine will be called in interrupt context. 33062306a36Sopenharmony_ci * Returns: 33162306a36Sopenharmony_ci * Success: Pointer to recv_info_t 33262306a36Sopenharmony_ci * Failure: NULL. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_cistatic inline struct octeon_recv_info *octeon_create_recv_info( 33562306a36Sopenharmony_ci struct octeon_device *octeon_dev, 33662306a36Sopenharmony_ci struct octeon_droq *droq, 33762306a36Sopenharmony_ci u32 buf_cnt, 33862306a36Sopenharmony_ci u32 idx) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct octeon_droq_info *info; 34162306a36Sopenharmony_ci struct octeon_recv_pkt *recv_pkt; 34262306a36Sopenharmony_ci struct octeon_recv_info *recv_info; 34362306a36Sopenharmony_ci u32 i, bytes_left; 34462306a36Sopenharmony_ci struct octeon_skb_page_info *pg_info; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci info = (struct octeon_droq_info *)droq->recv_buf_list[idx].data; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci recv_info = octeon_alloc_recv_info(sizeof(struct __dispatch)); 34962306a36Sopenharmony_ci if (!recv_info) 35062306a36Sopenharmony_ci return NULL; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci recv_pkt = recv_info->recv_pkt; 35362306a36Sopenharmony_ci recv_pkt->rh = info->rh; 35462306a36Sopenharmony_ci recv_pkt->length = (u32)info->length; 35562306a36Sopenharmony_ci recv_pkt->buffer_count = (u16)buf_cnt; 35662306a36Sopenharmony_ci recv_pkt->octeon_id = (u16)octeon_dev->octeon_id; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci i = 0; 35962306a36Sopenharmony_ci bytes_left = (u32)info->length; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci while (buf_cnt) { 36262306a36Sopenharmony_ci { 36362306a36Sopenharmony_ci pg_info = &droq->recv_buf_list[idx].pg_info; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci lio_unmap_ring(octeon_dev->pci_dev, 36662306a36Sopenharmony_ci (u64)pg_info->dma); 36762306a36Sopenharmony_ci pg_info->page = NULL; 36862306a36Sopenharmony_ci pg_info->dma = 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci recv_pkt->buffer_size[i] = 37262306a36Sopenharmony_ci (bytes_left >= 37362306a36Sopenharmony_ci droq->buffer_size) ? droq->buffer_size : bytes_left; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci recv_pkt->buffer_ptr[i] = droq->recv_buf_list[idx].buffer; 37662306a36Sopenharmony_ci droq->recv_buf_list[idx].buffer = NULL; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci idx = incr_index(idx, 1, droq->max_count); 37962306a36Sopenharmony_ci bytes_left -= droq->buffer_size; 38062306a36Sopenharmony_ci i++; 38162306a36Sopenharmony_ci buf_cnt--; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return recv_info; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* If we were not able to refill all buffers, try to move around 38862306a36Sopenharmony_ci * the buffers that were not dispatched. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_cistatic inline u32 39162306a36Sopenharmony_ciocteon_droq_refill_pullup_descs(struct octeon_droq *droq, 39262306a36Sopenharmony_ci struct octeon_droq_desc *desc_ring) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci u32 desc_refilled = 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci u32 refill_index = droq->refill_idx; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci while (refill_index != droq->read_idx) { 39962306a36Sopenharmony_ci if (droq->recv_buf_list[refill_index].buffer) { 40062306a36Sopenharmony_ci droq->recv_buf_list[droq->refill_idx].buffer = 40162306a36Sopenharmony_ci droq->recv_buf_list[refill_index].buffer; 40262306a36Sopenharmony_ci droq->recv_buf_list[droq->refill_idx].data = 40362306a36Sopenharmony_ci droq->recv_buf_list[refill_index].data; 40462306a36Sopenharmony_ci desc_ring[droq->refill_idx].buffer_ptr = 40562306a36Sopenharmony_ci desc_ring[refill_index].buffer_ptr; 40662306a36Sopenharmony_ci droq->recv_buf_list[refill_index].buffer = NULL; 40762306a36Sopenharmony_ci desc_ring[refill_index].buffer_ptr = 0; 40862306a36Sopenharmony_ci do { 40962306a36Sopenharmony_ci droq->refill_idx = incr_index(droq->refill_idx, 41062306a36Sopenharmony_ci 1, 41162306a36Sopenharmony_ci droq->max_count); 41262306a36Sopenharmony_ci desc_refilled++; 41362306a36Sopenharmony_ci droq->refill_count--; 41462306a36Sopenharmony_ci } while (droq->recv_buf_list[droq->refill_idx].buffer); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci refill_index = incr_index(refill_index, 1, droq->max_count); 41762306a36Sopenharmony_ci } /* while */ 41862306a36Sopenharmony_ci return desc_refilled; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* octeon_droq_refill 42262306a36Sopenharmony_ci * Parameters: 42362306a36Sopenharmony_ci * droq - droq in which descriptors require new buffers. 42462306a36Sopenharmony_ci * Description: 42562306a36Sopenharmony_ci * Called during normal DROQ processing in interrupt mode or by the poll 42662306a36Sopenharmony_ci * thread to refill the descriptors from which buffers were dispatched 42762306a36Sopenharmony_ci * to upper layers. Attempts to allocate new buffers. If that fails, moves 42862306a36Sopenharmony_ci * up buffers (that were not dispatched) to form a contiguous ring. 42962306a36Sopenharmony_ci * Returns: 43062306a36Sopenharmony_ci * No of descriptors refilled. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic u32 43362306a36Sopenharmony_ciocteon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct octeon_droq_desc *desc_ring; 43662306a36Sopenharmony_ci void *buf = NULL; 43762306a36Sopenharmony_ci u8 *data; 43862306a36Sopenharmony_ci u32 desc_refilled = 0; 43962306a36Sopenharmony_ci struct octeon_skb_page_info *pg_info; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci desc_ring = droq->desc_ring; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci while (droq->refill_count && (desc_refilled < droq->max_count)) { 44462306a36Sopenharmony_ci /* If a valid buffer exists (happens if there is no dispatch), 44562306a36Sopenharmony_ci * reuse the buffer, else allocate. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci if (!droq->recv_buf_list[droq->refill_idx].buffer) { 44862306a36Sopenharmony_ci pg_info = 44962306a36Sopenharmony_ci &droq->recv_buf_list[droq->refill_idx].pg_info; 45062306a36Sopenharmony_ci /* Either recycle the existing pages or go for 45162306a36Sopenharmony_ci * new page alloc 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci if (pg_info->page) 45462306a36Sopenharmony_ci buf = recv_buffer_reuse(octeon_dev, pg_info); 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci buf = recv_buffer_alloc(octeon_dev, pg_info); 45762306a36Sopenharmony_ci /* If a buffer could not be allocated, no point in 45862306a36Sopenharmony_ci * continuing 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci if (!buf) { 46162306a36Sopenharmony_ci droq->stats.rx_alloc_failure++; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci droq->recv_buf_list[droq->refill_idx].buffer = 46562306a36Sopenharmony_ci buf; 46662306a36Sopenharmony_ci data = get_rbd(buf); 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci data = get_rbd(droq->recv_buf_list 46962306a36Sopenharmony_ci [droq->refill_idx].buffer); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci droq->recv_buf_list[droq->refill_idx].data = data; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci desc_ring[droq->refill_idx].buffer_ptr = 47562306a36Sopenharmony_ci lio_map_ring(droq->recv_buf_list[ 47662306a36Sopenharmony_ci droq->refill_idx].buffer); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci droq->refill_idx = incr_index(droq->refill_idx, 1, 47962306a36Sopenharmony_ci droq->max_count); 48062306a36Sopenharmony_ci desc_refilled++; 48162306a36Sopenharmony_ci droq->refill_count--; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (droq->refill_count) 48562306a36Sopenharmony_ci desc_refilled += 48662306a36Sopenharmony_ci octeon_droq_refill_pullup_descs(droq, desc_ring); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* if droq->refill_count 48962306a36Sopenharmony_ci * The refill count would not change in pass two. We only moved buffers 49062306a36Sopenharmony_ci * to close the gap in the ring, but we would still have the same no. of 49162306a36Sopenharmony_ci * buffers to refill. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci return desc_refilled; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** check if we can allocate packets to get out of oom. 49762306a36Sopenharmony_ci * @param droq - Droq being checked. 49862306a36Sopenharmony_ci * @return 1 if fails to refill minimum 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ciint octeon_retry_droq_refill(struct octeon_droq *droq) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct octeon_device *oct = droq->oct_dev; 50362306a36Sopenharmony_ci int desc_refilled, reschedule = 1; 50462306a36Sopenharmony_ci u32 pkts_credit; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pkts_credit = readl(droq->pkts_credit_reg); 50762306a36Sopenharmony_ci desc_refilled = octeon_droq_refill(oct, droq); 50862306a36Sopenharmony_ci if (desc_refilled) { 50962306a36Sopenharmony_ci /* Flush the droq descriptor data to memory to be sure 51062306a36Sopenharmony_ci * that when we update the credits the data in memory 51162306a36Sopenharmony_ci * is accurate. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci wmb(); 51462306a36Sopenharmony_ci writel(desc_refilled, droq->pkts_credit_reg); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (pkts_credit + desc_refilled >= CN23XX_SLI_DEF_BP) 51762306a36Sopenharmony_ci reschedule = 0; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return reschedule; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic inline u32 52462306a36Sopenharmony_ciocteon_droq_get_bufcount(u32 buf_size, u32 total_len) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci return DIV_ROUND_UP(total_len, buf_size); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int 53062306a36Sopenharmony_ciocteon_droq_dispatch_pkt(struct octeon_device *oct, 53162306a36Sopenharmony_ci struct octeon_droq *droq, 53262306a36Sopenharmony_ci union octeon_rh *rh, 53362306a36Sopenharmony_ci struct octeon_droq_info *info) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci u32 cnt; 53662306a36Sopenharmony_ci octeon_dispatch_fn_t disp_fn; 53762306a36Sopenharmony_ci struct octeon_recv_info *rinfo; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci cnt = octeon_droq_get_bufcount(droq->buffer_size, (u32)info->length); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci disp_fn = octeon_get_dispatch(oct, (u16)rh->r.opcode, 54262306a36Sopenharmony_ci (u16)rh->r.subcode); 54362306a36Sopenharmony_ci if (disp_fn) { 54462306a36Sopenharmony_ci rinfo = octeon_create_recv_info(oct, droq, cnt, droq->read_idx); 54562306a36Sopenharmony_ci if (rinfo) { 54662306a36Sopenharmony_ci struct __dispatch *rdisp = rinfo->rsvd; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci rdisp->rinfo = rinfo; 54962306a36Sopenharmony_ci rdisp->disp_fn = disp_fn; 55062306a36Sopenharmony_ci rinfo->recv_pkt->rh = *rh; 55162306a36Sopenharmony_ci list_add_tail(&rdisp->list, 55262306a36Sopenharmony_ci &droq->dispatch_list); 55362306a36Sopenharmony_ci } else { 55462306a36Sopenharmony_ci droq->stats.dropped_nomem++; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "DROQ: No dispatch function (opcode %u/%u)\n", 55862306a36Sopenharmony_ci (unsigned int)rh->r.opcode, 55962306a36Sopenharmony_ci (unsigned int)rh->r.subcode); 56062306a36Sopenharmony_ci droq->stats.dropped_nodispatch++; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return cnt; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic inline void octeon_droq_drop_packets(struct octeon_device *oct, 56762306a36Sopenharmony_ci struct octeon_droq *droq, 56862306a36Sopenharmony_ci u32 cnt) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci u32 i = 0, buf_cnt; 57162306a36Sopenharmony_ci struct octeon_droq_info *info; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 57462306a36Sopenharmony_ci info = (struct octeon_droq_info *) 57562306a36Sopenharmony_ci droq->recv_buf_list[droq->read_idx].data; 57662306a36Sopenharmony_ci octeon_swap_8B_data((u64 *)info, 2); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (info->length) { 57962306a36Sopenharmony_ci info->length += OCTNET_FRM_LENGTH_SIZE; 58062306a36Sopenharmony_ci droq->stats.bytes_received += info->length; 58162306a36Sopenharmony_ci buf_cnt = octeon_droq_get_bufcount(droq->buffer_size, 58262306a36Sopenharmony_ci (u32)info->length); 58362306a36Sopenharmony_ci } else { 58462306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "DROQ: In drop: pkt with len 0\n"); 58562306a36Sopenharmony_ci buf_cnt = 1; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci droq->read_idx = incr_index(droq->read_idx, buf_cnt, 58962306a36Sopenharmony_ci droq->max_count); 59062306a36Sopenharmony_ci droq->refill_count += buf_cnt; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic u32 59562306a36Sopenharmony_ciocteon_droq_fast_process_packets(struct octeon_device *oct, 59662306a36Sopenharmony_ci struct octeon_droq *droq, 59762306a36Sopenharmony_ci u32 pkts_to_process) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci u32 pkt, total_len = 0, pkt_count, retval; 60062306a36Sopenharmony_ci struct octeon_droq_info *info; 60162306a36Sopenharmony_ci union octeon_rh *rh; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci pkt_count = pkts_to_process; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci for (pkt = 0; pkt < pkt_count; pkt++) { 60662306a36Sopenharmony_ci u32 pkt_len = 0; 60762306a36Sopenharmony_ci struct sk_buff *nicbuf = NULL; 60862306a36Sopenharmony_ci struct octeon_skb_page_info *pg_info; 60962306a36Sopenharmony_ci void *buf; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci info = (struct octeon_droq_info *) 61262306a36Sopenharmony_ci droq->recv_buf_list[droq->read_idx].data; 61362306a36Sopenharmony_ci octeon_swap_8B_data((u64 *)info, 2); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!info->length) { 61662306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, 61762306a36Sopenharmony_ci "DROQ[%d] idx: %d len:0, pkt_cnt: %d\n", 61862306a36Sopenharmony_ci droq->q_no, droq->read_idx, pkt_count); 61962306a36Sopenharmony_ci print_hex_dump_bytes("", DUMP_PREFIX_ADDRESS, 62062306a36Sopenharmony_ci (u8 *)info, 62162306a36Sopenharmony_ci OCT_DROQ_INFO_SIZE); 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* Len of resp hdr in included in the received data len. */ 62662306a36Sopenharmony_ci rh = &info->rh; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci info->length += OCTNET_FRM_LENGTH_SIZE; 62962306a36Sopenharmony_ci rh->r_dh.len += (ROUNDUP8(OCT_DROQ_INFO_SIZE) / sizeof(u64)); 63062306a36Sopenharmony_ci total_len += (u32)info->length; 63162306a36Sopenharmony_ci if (opcode_slow_path(rh)) { 63262306a36Sopenharmony_ci u32 buf_cnt; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci buf_cnt = octeon_droq_dispatch_pkt(oct, droq, rh, info); 63562306a36Sopenharmony_ci droq->read_idx = incr_index(droq->read_idx, 63662306a36Sopenharmony_ci buf_cnt, droq->max_count); 63762306a36Sopenharmony_ci droq->refill_count += buf_cnt; 63862306a36Sopenharmony_ci } else { 63962306a36Sopenharmony_ci if (info->length <= droq->buffer_size) { 64062306a36Sopenharmony_ci pkt_len = (u32)info->length; 64162306a36Sopenharmony_ci nicbuf = droq->recv_buf_list[ 64262306a36Sopenharmony_ci droq->read_idx].buffer; 64362306a36Sopenharmony_ci pg_info = &droq->recv_buf_list[ 64462306a36Sopenharmony_ci droq->read_idx].pg_info; 64562306a36Sopenharmony_ci if (recv_buffer_recycle(oct, pg_info)) 64662306a36Sopenharmony_ci pg_info->page = NULL; 64762306a36Sopenharmony_ci droq->recv_buf_list[droq->read_idx].buffer = 64862306a36Sopenharmony_ci NULL; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci droq->read_idx = incr_index(droq->read_idx, 1, 65162306a36Sopenharmony_ci droq->max_count); 65262306a36Sopenharmony_ci droq->refill_count++; 65362306a36Sopenharmony_ci } else { 65462306a36Sopenharmony_ci nicbuf = octeon_fast_packet_alloc((u32) 65562306a36Sopenharmony_ci info->length); 65662306a36Sopenharmony_ci pkt_len = 0; 65762306a36Sopenharmony_ci /* nicbuf allocation can fail. We'll handle it 65862306a36Sopenharmony_ci * inside the loop. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci while (pkt_len < info->length) { 66162306a36Sopenharmony_ci int cpy_len, idx = droq->read_idx; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci cpy_len = ((pkt_len + droq->buffer_size) 66462306a36Sopenharmony_ci > info->length) ? 66562306a36Sopenharmony_ci ((u32)info->length - pkt_len) : 66662306a36Sopenharmony_ci droq->buffer_size; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (nicbuf) { 66962306a36Sopenharmony_ci octeon_fast_packet_next(droq, 67062306a36Sopenharmony_ci nicbuf, 67162306a36Sopenharmony_ci cpy_len, 67262306a36Sopenharmony_ci idx); 67362306a36Sopenharmony_ci buf = droq->recv_buf_list[ 67462306a36Sopenharmony_ci idx].buffer; 67562306a36Sopenharmony_ci recv_buffer_fast_free(buf); 67662306a36Sopenharmony_ci droq->recv_buf_list[idx].buffer 67762306a36Sopenharmony_ci = NULL; 67862306a36Sopenharmony_ci } else { 67962306a36Sopenharmony_ci droq->stats.rx_alloc_failure++; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci pkt_len += cpy_len; 68362306a36Sopenharmony_ci droq->read_idx = 68462306a36Sopenharmony_ci incr_index(droq->read_idx, 1, 68562306a36Sopenharmony_ci droq->max_count); 68662306a36Sopenharmony_ci droq->refill_count++; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (nicbuf) { 69162306a36Sopenharmony_ci if (droq->ops.fptr) { 69262306a36Sopenharmony_ci droq->ops.fptr(oct->octeon_id, 69362306a36Sopenharmony_ci nicbuf, pkt_len, 69462306a36Sopenharmony_ci rh, &droq->napi, 69562306a36Sopenharmony_ci droq->ops.farg); 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci recv_buffer_free(nicbuf); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (droq->refill_count >= droq->refill_threshold) { 70362306a36Sopenharmony_ci int desc_refilled = octeon_droq_refill(oct, droq); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (desc_refilled) { 70662306a36Sopenharmony_ci /* Flush the droq descriptor data to memory to 70762306a36Sopenharmony_ci * be sure that when we update the credits the 70862306a36Sopenharmony_ci * data in memory is accurate. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci wmb(); 71162306a36Sopenharmony_ci writel(desc_refilled, droq->pkts_credit_reg); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci } /* for (each packet)... */ 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Increment refill_count by the number of buffers processed. */ 71762306a36Sopenharmony_ci droq->stats.pkts_received += pkt; 71862306a36Sopenharmony_ci droq->stats.bytes_received += total_len; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci retval = pkt; 72162306a36Sopenharmony_ci if ((droq->ops.drop_on_max) && (pkts_to_process - pkt)) { 72262306a36Sopenharmony_ci octeon_droq_drop_packets(oct, droq, (pkts_to_process - pkt)); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci droq->stats.dropped_toomany += (pkts_to_process - pkt); 72562306a36Sopenharmony_ci retval = pkts_to_process; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci atomic_sub(retval, &droq->pkts_pending); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (droq->refill_count >= droq->refill_threshold && 73162306a36Sopenharmony_ci readl(droq->pkts_credit_reg) < CN23XX_SLI_DEF_BP) { 73262306a36Sopenharmony_ci octeon_droq_check_hw_for_pkts(droq); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Make sure there are no pkts_pending */ 73562306a36Sopenharmony_ci if (!atomic_read(&droq->pkts_pending)) 73662306a36Sopenharmony_ci octeon_schedule_rxq_oom_work(oct, droq); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return retval; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ciint 74362306a36Sopenharmony_ciocteon_droq_process_packets(struct octeon_device *oct, 74462306a36Sopenharmony_ci struct octeon_droq *droq, 74562306a36Sopenharmony_ci u32 budget) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci u32 pkt_count = 0; 74862306a36Sopenharmony_ci struct list_head *tmp, *tmp2; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci octeon_droq_check_hw_for_pkts(droq); 75162306a36Sopenharmony_ci pkt_count = atomic_read(&droq->pkts_pending); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (!pkt_count) 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (pkt_count > budget) 75762306a36Sopenharmony_ci pkt_count = budget; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci octeon_droq_fast_process_packets(oct, droq, pkt_count); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci list_for_each_safe(tmp, tmp2, &droq->dispatch_list) { 76262306a36Sopenharmony_ci struct __dispatch *rdisp = (struct __dispatch *)tmp; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci list_del(tmp); 76562306a36Sopenharmony_ci rdisp->disp_fn(rdisp->rinfo, 76662306a36Sopenharmony_ci octeon_get_dispatch_arg 76762306a36Sopenharmony_ci (oct, 76862306a36Sopenharmony_ci (u16)rdisp->rinfo->recv_pkt->rh.r.opcode, 76962306a36Sopenharmony_ci (u16)rdisp->rinfo->recv_pkt->rh.r.subcode)); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* If there are packets pending. schedule tasklet again */ 77362306a36Sopenharmony_ci if (atomic_read(&droq->pkts_pending)) 77462306a36Sopenharmony_ci return 1; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return 0; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_droq_process_packets); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * Utility function to poll for packets. check_hw_for_packets must be 78262306a36Sopenharmony_ci * called before calling this routine. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciint 78662306a36Sopenharmony_ciocteon_droq_process_poll_pkts(struct octeon_device *oct, 78762306a36Sopenharmony_ci struct octeon_droq *droq, u32 budget) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct list_head *tmp, *tmp2; 79062306a36Sopenharmony_ci u32 pkts_available = 0, pkts_processed = 0; 79162306a36Sopenharmony_ci u32 total_pkts_processed = 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (budget > droq->max_count) 79462306a36Sopenharmony_ci budget = droq->max_count; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci while (total_pkts_processed < budget) { 79762306a36Sopenharmony_ci octeon_droq_check_hw_for_pkts(droq); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci pkts_available = min((budget - total_pkts_processed), 80062306a36Sopenharmony_ci (u32)(atomic_read(&droq->pkts_pending))); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (pkts_available == 0) 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci pkts_processed = 80662306a36Sopenharmony_ci octeon_droq_fast_process_packets(oct, droq, 80762306a36Sopenharmony_ci pkts_available); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci total_pkts_processed += pkts_processed; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci list_for_each_safe(tmp, tmp2, &droq->dispatch_list) { 81362306a36Sopenharmony_ci struct __dispatch *rdisp = (struct __dispatch *)tmp; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci list_del(tmp); 81662306a36Sopenharmony_ci rdisp->disp_fn(rdisp->rinfo, 81762306a36Sopenharmony_ci octeon_get_dispatch_arg 81862306a36Sopenharmony_ci (oct, 81962306a36Sopenharmony_ci (u16)rdisp->rinfo->recv_pkt->rh.r.opcode, 82062306a36Sopenharmony_ci (u16)rdisp->rinfo->recv_pkt->rh.r.subcode)); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return total_pkts_processed; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* Enable Pkt Interrupt */ 82762306a36Sopenharmony_ciint 82862306a36Sopenharmony_ciocteon_enable_irq(struct octeon_device *oct, u32 q_no) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci switch (oct->chip_id) { 83162306a36Sopenharmony_ci case OCTEON_CN66XX: 83262306a36Sopenharmony_ci case OCTEON_CN68XX: { 83362306a36Sopenharmony_ci struct octeon_cn6xxx *cn6xxx = 83462306a36Sopenharmony_ci (struct octeon_cn6xxx *)oct->chip; 83562306a36Sopenharmony_ci unsigned long flags; 83662306a36Sopenharmony_ci u32 value; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci spin_lock_irqsave 83962306a36Sopenharmony_ci (&cn6xxx->lock_for_droq_int_enb_reg, flags); 84062306a36Sopenharmony_ci value = octeon_read_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB); 84162306a36Sopenharmony_ci value |= (1 << q_no); 84262306a36Sopenharmony_ci octeon_write_csr(oct, CN6XXX_SLI_PKT_TIME_INT_ENB, value); 84362306a36Sopenharmony_ci value = octeon_read_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB); 84462306a36Sopenharmony_ci value |= (1 << q_no); 84562306a36Sopenharmony_ci octeon_write_csr(oct, CN6XXX_SLI_PKT_CNT_INT_ENB, value); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* don't bother flushing the enables */ 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci spin_unlock_irqrestore 85062306a36Sopenharmony_ci (&cn6xxx->lock_for_droq_int_enb_reg, flags); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci case OCTEON_CN23XX_PF_VID: 85462306a36Sopenharmony_ci lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]); 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci case OCTEON_CN23XX_VF_VID: 85862306a36Sopenharmony_ci lio_enable_irq(oct->droq[q_no], oct->instr_queue[q_no]); 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci default: 86162306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s Unknown Chip\n", __func__); 86262306a36Sopenharmony_ci return 1; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ciint octeon_register_droq_ops(struct octeon_device *oct, u32 q_no, 86962306a36Sopenharmony_ci struct octeon_droq_ops *ops) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct octeon_config *oct_cfg = NULL; 87262306a36Sopenharmony_ci struct octeon_droq *droq; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci oct_cfg = octeon_get_conf(oct); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!oct_cfg) 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!(ops)) { 88062306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s: droq_ops pointer is NULL\n", 88162306a36Sopenharmony_ci __func__); 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (q_no >= CFG_GET_OQ_MAX_Q(oct_cfg)) { 88662306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s: droq id (%d) exceeds MAX (%d)\n", 88762306a36Sopenharmony_ci __func__, q_no, (oct->num_oqs - 1)); 88862306a36Sopenharmony_ci return -EINVAL; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci droq = oct->droq[q_no]; 89262306a36Sopenharmony_ci memcpy(&droq->ops, ops, sizeof(struct octeon_droq_ops)); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ciint octeon_unregister_droq_ops(struct octeon_device *oct, u32 q_no) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct octeon_config *oct_cfg = NULL; 90062306a36Sopenharmony_ci struct octeon_droq *droq; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci oct_cfg = octeon_get_conf(oct); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (!oct_cfg) 90562306a36Sopenharmony_ci return -EINVAL; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (q_no >= CFG_GET_OQ_MAX_Q(oct_cfg)) { 90862306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s: droq id (%d) exceeds MAX (%d)\n", 90962306a36Sopenharmony_ci __func__, q_no, oct->num_oqs - 1); 91062306a36Sopenharmony_ci return -EINVAL; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci droq = oct->droq[q_no]; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!droq) { 91662306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, 91762306a36Sopenharmony_ci "Droq id (%d) not available.\n", q_no); 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci droq->ops.fptr = NULL; 92262306a36Sopenharmony_ci droq->ops.farg = NULL; 92362306a36Sopenharmony_ci droq->ops.drop_on_max = 0; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_unregister_droq_ops); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ciint octeon_create_droq(struct octeon_device *oct, 93062306a36Sopenharmony_ci u32 q_no, u32 num_descs, 93162306a36Sopenharmony_ci u32 desc_size, void *app_ctx) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct octeon_droq *droq; 93462306a36Sopenharmony_ci int numa_node = dev_to_node(&oct->pci_dev->dev); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (oct->droq[q_no]) { 93762306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "Droq already in use. Cannot create droq %d again\n", 93862306a36Sopenharmony_ci q_no); 93962306a36Sopenharmony_ci return 1; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Allocate the DS for the new droq. */ 94362306a36Sopenharmony_ci droq = vmalloc_node(sizeof(*droq), numa_node); 94462306a36Sopenharmony_ci if (!droq) 94562306a36Sopenharmony_ci droq = vmalloc(sizeof(*droq)); 94662306a36Sopenharmony_ci if (!droq) 94762306a36Sopenharmony_ci return -1; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci memset(droq, 0, sizeof(struct octeon_droq)); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /*Disable the pkt o/p for this Q */ 95262306a36Sopenharmony_ci octeon_set_droq_pkt_op(oct, q_no, 0); 95362306a36Sopenharmony_ci oct->droq[q_no] = droq; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* Initialize the Droq */ 95662306a36Sopenharmony_ci if (octeon_init_droq(oct, q_no, num_descs, desc_size, app_ctx)) { 95762306a36Sopenharmony_ci vfree(oct->droq[q_no]); 95862306a36Sopenharmony_ci oct->droq[q_no] = NULL; 95962306a36Sopenharmony_ci return -1; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci oct->num_oqs++; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "%s: Total number of OQ: %d\n", __func__, 96562306a36Sopenharmony_ci oct->num_oqs); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* Global Droq register settings */ 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* As of now not required, as setting are done for all 32 Droqs at 97062306a36Sopenharmony_ci * the same time. 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 974