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 1762306a36Sopenharmony_ci * details. 1862306a36Sopenharmony_ci **********************************************************************/ 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/netdevice.h> 2162306a36Sopenharmony_ci#include <linux/vmalloc.h> 2262306a36Sopenharmony_ci#include "liquidio_common.h" 2362306a36Sopenharmony_ci#include "octeon_droq.h" 2462306a36Sopenharmony_ci#include "octeon_iq.h" 2562306a36Sopenharmony_ci#include "response_manager.h" 2662306a36Sopenharmony_ci#include "octeon_device.h" 2762306a36Sopenharmony_ci#include "octeon_main.h" 2862306a36Sopenharmony_ci#include "octeon_network.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 iq_post_status { 3462306a36Sopenharmony_ci int status; 3562306a36Sopenharmony_ci int index; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void check_db_timeout(struct work_struct *work); 3962306a36Sopenharmony_cistatic void __check_db_timeout(struct octeon_device *oct, u64 iq_no); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void (*reqtype_free_fn[MAX_OCTEON_DEVICES][REQTYPE_LAST + 1]) (void *); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Define this to return the request status comaptible to old code */ 4462306a36Sopenharmony_ci/*#define OCTEON_USE_OLD_REQ_STATUS*/ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Return 0 on success, 1 on failure */ 4762306a36Sopenharmony_ciint octeon_init_instr_queue(struct octeon_device *oct, 4862306a36Sopenharmony_ci union oct_txpciq txpciq, 4962306a36Sopenharmony_ci u32 num_descs) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct octeon_instr_queue *iq; 5262306a36Sopenharmony_ci struct octeon_iq_config *conf = NULL; 5362306a36Sopenharmony_ci u32 iq_no = (u32)txpciq.s.q_no; 5462306a36Sopenharmony_ci u32 q_size; 5562306a36Sopenharmony_ci struct cavium_wq *db_wq; 5662306a36Sopenharmony_ci int numa_node = dev_to_node(&oct->pci_dev->dev); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (OCTEON_CN6XXX(oct)) 5962306a36Sopenharmony_ci conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx))); 6062306a36Sopenharmony_ci else if (OCTEON_CN23XX_PF(oct)) 6162306a36Sopenharmony_ci conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_pf))); 6262306a36Sopenharmony_ci else if (OCTEON_CN23XX_VF(oct)) 6362306a36Sopenharmony_ci conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn23xx_vf))); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!conf) { 6662306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Unsupported Chip %x\n", 6762306a36Sopenharmony_ci oct->chip_id); 6862306a36Sopenharmony_ci return 1; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci q_size = (u32)conf->instr_type * num_descs; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci iq = oct->instr_queue[iq_no]; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci iq->oct_dev = oct; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci iq->base_addr = lio_dma_alloc(oct, q_size, &iq->base_addr_dma); 7862306a36Sopenharmony_ci if (!iq->base_addr) { 7962306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Cannot allocate memory for instr queue %d\n", 8062306a36Sopenharmony_ci iq_no); 8162306a36Sopenharmony_ci return 1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci iq->max_count = num_descs; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Initialize a list to holds requests that have been posted to Octeon 8762306a36Sopenharmony_ci * but has yet to be fetched by octeon 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci iq->request_list = vzalloc_node(array_size(num_descs, sizeof(*iq->request_list)), 9062306a36Sopenharmony_ci numa_node); 9162306a36Sopenharmony_ci if (!iq->request_list) 9262306a36Sopenharmony_ci iq->request_list = vzalloc(array_size(num_descs, sizeof(*iq->request_list))); 9362306a36Sopenharmony_ci if (!iq->request_list) { 9462306a36Sopenharmony_ci lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma); 9562306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Alloc failed for IQ[%d] nr free list\n", 9662306a36Sopenharmony_ci iq_no); 9762306a36Sopenharmony_ci return 1; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "IQ[%d]: base: %p basedma: %pad count: %d\n", 10162306a36Sopenharmony_ci iq_no, iq->base_addr, &iq->base_addr_dma, iq->max_count); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci iq->txpciq.u64 = txpciq.u64; 10462306a36Sopenharmony_ci iq->fill_threshold = (u32)conf->db_min; 10562306a36Sopenharmony_ci iq->fill_cnt = 0; 10662306a36Sopenharmony_ci iq->host_write_index = 0; 10762306a36Sopenharmony_ci iq->octeon_read_index = 0; 10862306a36Sopenharmony_ci iq->flush_index = 0; 10962306a36Sopenharmony_ci iq->last_db_time = 0; 11062306a36Sopenharmony_ci iq->do_auto_flush = 1; 11162306a36Sopenharmony_ci iq->db_timeout = (u32)conf->db_timeout; 11262306a36Sopenharmony_ci atomic_set(&iq->instr_pending, 0); 11362306a36Sopenharmony_ci iq->pkts_processed = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Initialize the spinlock for this instruction queue */ 11662306a36Sopenharmony_ci spin_lock_init(&iq->lock); 11762306a36Sopenharmony_ci if (iq_no == 0) { 11862306a36Sopenharmony_ci iq->allow_soft_cmds = true; 11962306a36Sopenharmony_ci spin_lock_init(&iq->post_lock); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci iq->allow_soft_cmds = false; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock_init(&iq->iq_flush_running_lock); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci oct->io_qmask.iq |= BIT_ULL(iq_no); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Set the 32B/64B mode for each input queue */ 12962306a36Sopenharmony_ci oct->io_qmask.iq64B |= ((conf->instr_type == 64) << iq_no); 13062306a36Sopenharmony_ci iq->iqcmd_64B = (conf->instr_type == 64); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci oct->fn_list.setup_iq_regs(oct, iq_no); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci oct->check_db_wq[iq_no].wq = alloc_workqueue("check_iq_db", 13562306a36Sopenharmony_ci WQ_MEM_RECLAIM, 13662306a36Sopenharmony_ci 0); 13762306a36Sopenharmony_ci if (!oct->check_db_wq[iq_no].wq) { 13862306a36Sopenharmony_ci vfree(iq->request_list); 13962306a36Sopenharmony_ci iq->request_list = NULL; 14062306a36Sopenharmony_ci lio_dma_free(oct, q_size, iq->base_addr, iq->base_addr_dma); 14162306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "check db wq create failed for iq %d\n", 14262306a36Sopenharmony_ci iq_no); 14362306a36Sopenharmony_ci return 1; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci db_wq = &oct->check_db_wq[iq_no]; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci INIT_DELAYED_WORK(&db_wq->wk.work, check_db_timeout); 14962306a36Sopenharmony_ci db_wq->wk.ctxptr = oct; 15062306a36Sopenharmony_ci db_wq->wk.ctxul = iq_no; 15162306a36Sopenharmony_ci queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(1)); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciint octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u64 desc_size = 0, q_size; 15962306a36Sopenharmony_ci struct octeon_instr_queue *iq = oct->instr_queue[iq_no]; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci cancel_delayed_work_sync(&oct->check_db_wq[iq_no].wk.work); 16262306a36Sopenharmony_ci destroy_workqueue(oct->check_db_wq[iq_no].wq); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (OCTEON_CN6XXX(oct)) 16562306a36Sopenharmony_ci desc_size = 16662306a36Sopenharmony_ci CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn6xxx)); 16762306a36Sopenharmony_ci else if (OCTEON_CN23XX_PF(oct)) 16862306a36Sopenharmony_ci desc_size = 16962306a36Sopenharmony_ci CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_pf)); 17062306a36Sopenharmony_ci else if (OCTEON_CN23XX_VF(oct)) 17162306a36Sopenharmony_ci desc_size = 17262306a36Sopenharmony_ci CFG_GET_IQ_INSTR_TYPE(CHIP_CONF(oct, cn23xx_vf)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci vfree(iq->request_list); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (iq->base_addr) { 17762306a36Sopenharmony_ci q_size = iq->max_count * desc_size; 17862306a36Sopenharmony_ci lio_dma_free(oct, (u32)q_size, iq->base_addr, 17962306a36Sopenharmony_ci iq->base_addr_dma); 18062306a36Sopenharmony_ci oct->io_qmask.iq &= ~(1ULL << iq_no); 18162306a36Sopenharmony_ci vfree(oct->instr_queue[iq_no]); 18262306a36Sopenharmony_ci oct->instr_queue[iq_no] = NULL; 18362306a36Sopenharmony_ci oct->num_iqs--; 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci return 1; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_delete_instr_queue); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* Return 0 on success, 1 on failure */ 19162306a36Sopenharmony_ciint octeon_setup_iq(struct octeon_device *oct, 19262306a36Sopenharmony_ci int ifidx, 19362306a36Sopenharmony_ci int q_index, 19462306a36Sopenharmony_ci union oct_txpciq txpciq, 19562306a36Sopenharmony_ci u32 num_descs, 19662306a36Sopenharmony_ci void *app_ctx) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 iq_no = (u32)txpciq.s.q_no; 19962306a36Sopenharmony_ci int numa_node = dev_to_node(&oct->pci_dev->dev); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (oct->instr_queue[iq_no]) { 20262306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "IQ is in use. Cannot create the IQ: %d again\n", 20362306a36Sopenharmony_ci iq_no); 20462306a36Sopenharmony_ci oct->instr_queue[iq_no]->txpciq.u64 = txpciq.u64; 20562306a36Sopenharmony_ci oct->instr_queue[iq_no]->app_ctx = app_ctx; 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci oct->instr_queue[iq_no] = 20962306a36Sopenharmony_ci vzalloc_node(sizeof(struct octeon_instr_queue), numa_node); 21062306a36Sopenharmony_ci if (!oct->instr_queue[iq_no]) 21162306a36Sopenharmony_ci oct->instr_queue[iq_no] = 21262306a36Sopenharmony_ci vzalloc(sizeof(struct octeon_instr_queue)); 21362306a36Sopenharmony_ci if (!oct->instr_queue[iq_no]) 21462306a36Sopenharmony_ci return 1; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci oct->instr_queue[iq_no]->q_index = q_index; 21862306a36Sopenharmony_ci oct->instr_queue[iq_no]->app_ctx = app_ctx; 21962306a36Sopenharmony_ci oct->instr_queue[iq_no]->ifidx = ifidx; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (octeon_init_instr_queue(oct, txpciq, num_descs)) { 22262306a36Sopenharmony_ci vfree(oct->instr_queue[iq_no]); 22362306a36Sopenharmony_ci oct->instr_queue[iq_no] = NULL; 22462306a36Sopenharmony_ci return 1; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci oct->num_iqs++; 22862306a36Sopenharmony_ci if (oct->fn_list.enable_io_queues(oct)) { 22962306a36Sopenharmony_ci octeon_delete_instr_queue(oct, iq_no); 23062306a36Sopenharmony_ci return 1; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ciint lio_wait_for_instr_fetch(struct octeon_device *oct) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int i, retry = 1000, pending, instr_cnt = 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci do { 24162306a36Sopenharmony_ci instr_cnt = 0; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { 24462306a36Sopenharmony_ci if (!(oct->io_qmask.iq & BIT_ULL(i))) 24562306a36Sopenharmony_ci continue; 24662306a36Sopenharmony_ci pending = 24762306a36Sopenharmony_ci atomic_read(&oct->instr_queue[i]->instr_pending); 24862306a36Sopenharmony_ci if (pending) 24962306a36Sopenharmony_ci __check_db_timeout(oct, i); 25062306a36Sopenharmony_ci instr_cnt += pending; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (instr_cnt == 0) 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci } while (retry-- && instr_cnt); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return instr_cnt; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lio_wait_for_instr_fetch); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic inline void 26562306a36Sopenharmony_ciring_doorbell(struct octeon_device *oct, struct octeon_instr_queue *iq) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (atomic_read(&oct->status) == OCT_DEV_RUNNING) { 26862306a36Sopenharmony_ci writel(iq->fill_cnt, iq->doorbell_reg); 26962306a36Sopenharmony_ci /* make sure doorbell write goes through */ 27062306a36Sopenharmony_ci iq->fill_cnt = 0; 27162306a36Sopenharmony_ci iq->last_db_time = jiffies; 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_civoid 27762306a36Sopenharmony_ciocteon_ring_doorbell_locked(struct octeon_device *oct, u32 iq_no) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct octeon_instr_queue *iq; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci iq = oct->instr_queue[iq_no]; 28262306a36Sopenharmony_ci spin_lock(&iq->post_lock); 28362306a36Sopenharmony_ci if (iq->fill_cnt) 28462306a36Sopenharmony_ci ring_doorbell(oct, iq); 28562306a36Sopenharmony_ci spin_unlock(&iq->post_lock); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_ring_doorbell_locked); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic inline void __copy_cmd_into_iq(struct octeon_instr_queue *iq, 29062306a36Sopenharmony_ci u8 *cmd) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci u8 *iqptr, cmdsize; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci cmdsize = ((iq->iqcmd_64B) ? 64 : 32); 29562306a36Sopenharmony_ci iqptr = iq->base_addr + (cmdsize * iq->host_write_index); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci memcpy(iqptr, cmd, cmdsize); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline struct iq_post_status 30162306a36Sopenharmony_ci__post_command2(struct octeon_instr_queue *iq, u8 *cmd) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct iq_post_status st; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci st.status = IQ_SEND_OK; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* This ensures that the read index does not wrap around to the same 30862306a36Sopenharmony_ci * position if queue gets full before Octeon could fetch any instr. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 1)) { 31162306a36Sopenharmony_ci st.status = IQ_SEND_FAILED; 31262306a36Sopenharmony_ci st.index = -1; 31362306a36Sopenharmony_ci return st; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (atomic_read(&iq->instr_pending) >= (s32)(iq->max_count - 2)) 31762306a36Sopenharmony_ci st.status = IQ_SEND_STOP; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci __copy_cmd_into_iq(iq, cmd); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* "index" is returned, host_write_index is modified. */ 32262306a36Sopenharmony_ci st.index = iq->host_write_index; 32362306a36Sopenharmony_ci iq->host_write_index = incr_index(iq->host_write_index, 1, 32462306a36Sopenharmony_ci iq->max_count); 32562306a36Sopenharmony_ci iq->fill_cnt++; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Flush the command into memory. We need to be sure the data is in 32862306a36Sopenharmony_ci * memory before indicating that the instruction is pending. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci wmb(); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci atomic_inc(&iq->instr_pending); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return st; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint 33862306a36Sopenharmony_ciocteon_register_reqtype_free_fn(struct octeon_device *oct, int reqtype, 33962306a36Sopenharmony_ci void (*fn)(void *)) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci if (reqtype > REQTYPE_LAST) { 34262306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "%s: Invalid reqtype: %d\n", 34362306a36Sopenharmony_ci __func__, reqtype); 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci reqtype_free_fn[oct->octeon_id][reqtype] = fn; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_register_reqtype_free_fn); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic inline void 35462306a36Sopenharmony_ci__add_to_request_list(struct octeon_instr_queue *iq, 35562306a36Sopenharmony_ci int idx, void *buf, int reqtype) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci iq->request_list[idx].buf = buf; 35862306a36Sopenharmony_ci iq->request_list[idx].reqtype = reqtype; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* Can only run in process context */ 36262306a36Sopenharmony_ciint 36362306a36Sopenharmony_cilio_process_iq_request_list(struct octeon_device *oct, 36462306a36Sopenharmony_ci struct octeon_instr_queue *iq, u32 napi_budget) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct cavium_wq *cwq = &oct->dma_comp_wq; 36762306a36Sopenharmony_ci int reqtype; 36862306a36Sopenharmony_ci void *buf; 36962306a36Sopenharmony_ci u32 old = iq->flush_index; 37062306a36Sopenharmony_ci u32 inst_count = 0; 37162306a36Sopenharmony_ci unsigned int pkts_compl = 0, bytes_compl = 0; 37262306a36Sopenharmony_ci struct octeon_soft_command *sc; 37362306a36Sopenharmony_ci unsigned long flags; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci while (old != iq->octeon_read_index) { 37662306a36Sopenharmony_ci reqtype = iq->request_list[old].reqtype; 37762306a36Sopenharmony_ci buf = iq->request_list[old].buf; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (reqtype == REQTYPE_NONE) 38062306a36Sopenharmony_ci goto skip_this; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci octeon_update_tx_completion_counters(buf, reqtype, &pkts_compl, 38362306a36Sopenharmony_ci &bytes_compl); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci switch (reqtype) { 38662306a36Sopenharmony_ci case REQTYPE_NORESP_NET: 38762306a36Sopenharmony_ci case REQTYPE_NORESP_NET_SG: 38862306a36Sopenharmony_ci case REQTYPE_RESP_NET_SG: 38962306a36Sopenharmony_ci reqtype_free_fn[oct->octeon_id][reqtype](buf); 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case REQTYPE_RESP_NET: 39262306a36Sopenharmony_ci case REQTYPE_SOFT_COMMAND: 39362306a36Sopenharmony_ci sc = buf; 39462306a36Sopenharmony_ci /* We're expecting a response from Octeon. 39562306a36Sopenharmony_ci * It's up to lio_process_ordered_list() to 39662306a36Sopenharmony_ci * process sc. Add sc to the ordered soft 39762306a36Sopenharmony_ci * command response list because we expect 39862306a36Sopenharmony_ci * a response from Octeon. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci spin_lock_irqsave(&oct->response_list 40162306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].lock, flags); 40262306a36Sopenharmony_ci atomic_inc(&oct->response_list 40362306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].pending_req_count); 40462306a36Sopenharmony_ci list_add_tail(&sc->node, &oct->response_list 40562306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].head); 40662306a36Sopenharmony_ci spin_unlock_irqrestore(&oct->response_list 40762306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].lock, 40862306a36Sopenharmony_ci flags); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, 41262306a36Sopenharmony_ci "%s Unknown reqtype: %d buf: %p at idx %d\n", 41362306a36Sopenharmony_ci __func__, reqtype, buf, old); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci iq->request_list[old].buf = NULL; 41762306a36Sopenharmony_ci iq->request_list[old].reqtype = 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci skip_this: 42062306a36Sopenharmony_ci inst_count++; 42162306a36Sopenharmony_ci old = incr_index(old, 1, iq->max_count); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if ((napi_budget) && (inst_count >= napi_budget)) 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci if (bytes_compl) 42762306a36Sopenharmony_ci octeon_report_tx_completion_to_bql(iq->app_ctx, pkts_compl, 42862306a36Sopenharmony_ci bytes_compl); 42962306a36Sopenharmony_ci iq->flush_index = old; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (atomic_read(&oct->response_list 43262306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].pending_req_count)) 43362306a36Sopenharmony_ci queue_work(cwq->wq, &cwq->wk.work.work); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return inst_count; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lio_process_iq_request_list); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* Can only be called from process context */ 44062306a36Sopenharmony_ciint 44162306a36Sopenharmony_ciocteon_flush_iq(struct octeon_device *oct, struct octeon_instr_queue *iq, 44262306a36Sopenharmony_ci u32 napi_budget) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci u32 inst_processed = 0; 44562306a36Sopenharmony_ci u32 tot_inst_processed = 0; 44662306a36Sopenharmony_ci int tx_done = 1; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!spin_trylock(&iq->iq_flush_running_lock)) 44962306a36Sopenharmony_ci return tx_done; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci spin_lock_bh(&iq->lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci iq->octeon_read_index = oct->fn_list.update_iq_read_idx(iq); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci do { 45662306a36Sopenharmony_ci /* Process any outstanding IQ packets. */ 45762306a36Sopenharmony_ci if (iq->flush_index == iq->octeon_read_index) 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (napi_budget) 46162306a36Sopenharmony_ci inst_processed = 46262306a36Sopenharmony_ci lio_process_iq_request_list(oct, iq, 46362306a36Sopenharmony_ci napi_budget - 46462306a36Sopenharmony_ci tot_inst_processed); 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci inst_processed = 46762306a36Sopenharmony_ci lio_process_iq_request_list(oct, iq, 0); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (inst_processed) { 47062306a36Sopenharmony_ci iq->pkts_processed += inst_processed; 47162306a36Sopenharmony_ci atomic_sub(inst_processed, &iq->instr_pending); 47262306a36Sopenharmony_ci iq->stats.instr_processed += inst_processed; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci tot_inst_processed += inst_processed; 47662306a36Sopenharmony_ci } while (tot_inst_processed < napi_budget); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (napi_budget && (tot_inst_processed >= napi_budget)) 47962306a36Sopenharmony_ci tx_done = 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci iq->last_db_time = jiffies; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci spin_unlock_bh(&iq->lock); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_unlock(&iq->iq_flush_running_lock); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return tx_done; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* Process instruction queue after timeout. 49162306a36Sopenharmony_ci * This routine gets called from a workqueue or when removing the module. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_cistatic void __check_db_timeout(struct octeon_device *oct, u64 iq_no) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct octeon_instr_queue *iq; 49662306a36Sopenharmony_ci u64 next_time; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!oct) 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci iq = oct->instr_queue[iq_no]; 50262306a36Sopenharmony_ci if (!iq) 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* return immediately, if no work pending */ 50662306a36Sopenharmony_ci if (!atomic_read(&iq->instr_pending)) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci /* If jiffies - last_db_time < db_timeout do nothing */ 50962306a36Sopenharmony_ci next_time = iq->last_db_time + iq->db_timeout; 51062306a36Sopenharmony_ci if (!time_after(jiffies, (unsigned long)next_time)) 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci iq->last_db_time = jiffies; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Flush the instruction queue */ 51562306a36Sopenharmony_ci octeon_flush_iq(oct, iq, 0); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci lio_enable_irq(NULL, iq); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* Called by the Poll thread at regular intervals to check the instruction 52162306a36Sopenharmony_ci * queue for commands to be posted and for commands that were fetched by Octeon. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_cistatic void check_db_timeout(struct work_struct *work) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct cavium_wk *wk = (struct cavium_wk *)work; 52662306a36Sopenharmony_ci struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; 52762306a36Sopenharmony_ci u64 iq_no = wk->ctxul; 52862306a36Sopenharmony_ci struct cavium_wq *db_wq = &oct->check_db_wq[iq_no]; 52962306a36Sopenharmony_ci u32 delay = 10; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci __check_db_timeout(oct, iq_no); 53262306a36Sopenharmony_ci queue_delayed_work(db_wq->wq, &db_wq->wk.work, msecs_to_jiffies(delay)); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ciint 53662306a36Sopenharmony_ciocteon_send_command(struct octeon_device *oct, u32 iq_no, 53762306a36Sopenharmony_ci u32 force_db, void *cmd, void *buf, 53862306a36Sopenharmony_ci u32 datasize, u32 reqtype) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci int xmit_stopped; 54162306a36Sopenharmony_ci struct iq_post_status st; 54262306a36Sopenharmony_ci struct octeon_instr_queue *iq = oct->instr_queue[iq_no]; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Get the lock and prevent other tasks and tx interrupt handler from 54562306a36Sopenharmony_ci * running. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci if (iq->allow_soft_cmds) 54862306a36Sopenharmony_ci spin_lock_bh(&iq->post_lock); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci st = __post_command2(iq, cmd); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (st.status != IQ_SEND_FAILED) { 55362306a36Sopenharmony_ci xmit_stopped = octeon_report_sent_bytes_to_bql(buf, reqtype); 55462306a36Sopenharmony_ci __add_to_request_list(iq, st.index, buf, reqtype); 55562306a36Sopenharmony_ci INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, bytes_sent, datasize); 55662306a36Sopenharmony_ci INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_posted, 1); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (iq->fill_cnt >= MAX_OCTEON_FILL_COUNT || force_db || 55962306a36Sopenharmony_ci xmit_stopped || st.status == IQ_SEND_STOP) 56062306a36Sopenharmony_ci ring_doorbell(oct, iq); 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci INCR_INSTRQUEUE_PKT_COUNT(oct, iq_no, instr_dropped, 1); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (iq->allow_soft_cmds) 56662306a36Sopenharmony_ci spin_unlock_bh(&iq->post_lock); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* This is only done here to expedite packets being flushed 56962306a36Sopenharmony_ci * for cases where there are no IQ completion interrupts. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return st.status; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_send_command); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_civoid 57762306a36Sopenharmony_ciocteon_prepare_soft_command(struct octeon_device *oct, 57862306a36Sopenharmony_ci struct octeon_soft_command *sc, 57962306a36Sopenharmony_ci u8 opcode, 58062306a36Sopenharmony_ci u8 subcode, 58162306a36Sopenharmony_ci u32 irh_ossp, 58262306a36Sopenharmony_ci u64 ossp0, 58362306a36Sopenharmony_ci u64 ossp1) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct octeon_config *oct_cfg; 58662306a36Sopenharmony_ci struct octeon_instr_ih2 *ih2; 58762306a36Sopenharmony_ci struct octeon_instr_ih3 *ih3; 58862306a36Sopenharmony_ci struct octeon_instr_pki_ih3 *pki_ih3; 58962306a36Sopenharmony_ci struct octeon_instr_irh *irh; 59062306a36Sopenharmony_ci struct octeon_instr_rdp *rdp; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci WARN_ON(opcode > 15); 59362306a36Sopenharmony_ci WARN_ON(subcode > 127); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci oct_cfg = octeon_get_conf(oct); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { 59862306a36Sopenharmony_ci ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci pki_ih3->w = 1; 60562306a36Sopenharmony_ci pki_ih3->raw = 1; 60662306a36Sopenharmony_ci pki_ih3->utag = 1; 60762306a36Sopenharmony_ci pki_ih3->uqpg = 60862306a36Sopenharmony_ci oct->instr_queue[sc->iq_no]->txpciq.s.use_qpg; 60962306a36Sopenharmony_ci pki_ih3->utt = 1; 61062306a36Sopenharmony_ci pki_ih3->tag = LIO_CONTROL; 61162306a36Sopenharmony_ci pki_ih3->tagtype = ATOMIC_TAG; 61262306a36Sopenharmony_ci pki_ih3->qpg = 61362306a36Sopenharmony_ci oct->instr_queue[sc->iq_no]->txpciq.s.ctrl_qpg; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci pki_ih3->pm = 0x7; 61662306a36Sopenharmony_ci pki_ih3->sl = 8; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (sc->datasize) 61962306a36Sopenharmony_ci ih3->dlengsz = sc->datasize; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh; 62262306a36Sopenharmony_ci irh->opcode = opcode; 62362306a36Sopenharmony_ci irh->subcode = subcode; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* opcode/subcode specific parameters (ossp) */ 62662306a36Sopenharmony_ci irh->ossp = irh_ossp; 62762306a36Sopenharmony_ci sc->cmd.cmd3.ossp[0] = ossp0; 62862306a36Sopenharmony_ci sc->cmd.cmd3.ossp[1] = ossp1; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (sc->rdatasize) { 63162306a36Sopenharmony_ci rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp; 63262306a36Sopenharmony_ci rdp->pcie_port = oct->pcie_port; 63362306a36Sopenharmony_ci rdp->rlen = sc->rdatasize; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci irh->rflag = 1; 63662306a36Sopenharmony_ci /*PKI IH3*/ 63762306a36Sopenharmony_ci /* pki_ih3 irh+ossp[0]+ossp[1]+rdp+rptr = 48 bytes */ 63862306a36Sopenharmony_ci ih3->fsz = LIO_SOFTCMDRESP_IH3; 63962306a36Sopenharmony_ci } else { 64062306a36Sopenharmony_ci irh->rflag = 0; 64162306a36Sopenharmony_ci /*PKI IH3*/ 64262306a36Sopenharmony_ci /* pki_h3 + irh + ossp[0] + ossp[1] = 32 bytes */ 64362306a36Sopenharmony_ci ih3->fsz = LIO_PCICMD_O3; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci } else { 64762306a36Sopenharmony_ci ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2; 64862306a36Sopenharmony_ci ih2->tagtype = ATOMIC_TAG; 64962306a36Sopenharmony_ci ih2->tag = LIO_CONTROL; 65062306a36Sopenharmony_ci ih2->raw = 1; 65162306a36Sopenharmony_ci ih2->grp = CFG_GET_CTRL_Q_GRP(oct_cfg); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (sc->datasize) { 65462306a36Sopenharmony_ci ih2->dlengsz = sc->datasize; 65562306a36Sopenharmony_ci ih2->rs = 1; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh; 65962306a36Sopenharmony_ci irh->opcode = opcode; 66062306a36Sopenharmony_ci irh->subcode = subcode; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* opcode/subcode specific parameters (ossp) */ 66362306a36Sopenharmony_ci irh->ossp = irh_ossp; 66462306a36Sopenharmony_ci sc->cmd.cmd2.ossp[0] = ossp0; 66562306a36Sopenharmony_ci sc->cmd.cmd2.ossp[1] = ossp1; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (sc->rdatasize) { 66862306a36Sopenharmony_ci rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp; 66962306a36Sopenharmony_ci rdp->pcie_port = oct->pcie_port; 67062306a36Sopenharmony_ci rdp->rlen = sc->rdatasize; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci irh->rflag = 1; 67362306a36Sopenharmony_ci /* irh+ossp[0]+ossp[1]+rdp+rptr = 40 bytes */ 67462306a36Sopenharmony_ci ih2->fsz = LIO_SOFTCMDRESP_IH2; 67562306a36Sopenharmony_ci } else { 67662306a36Sopenharmony_ci irh->rflag = 0; 67762306a36Sopenharmony_ci /* irh + ossp[0] + ossp[1] = 24 bytes */ 67862306a36Sopenharmony_ci ih2->fsz = LIO_PCICMD_O2; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_prepare_soft_command); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ciint octeon_send_soft_command(struct octeon_device *oct, 68562306a36Sopenharmony_ci struct octeon_soft_command *sc) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct octeon_instr_queue *iq; 68862306a36Sopenharmony_ci struct octeon_instr_ih2 *ih2; 68962306a36Sopenharmony_ci struct octeon_instr_ih3 *ih3; 69062306a36Sopenharmony_ci struct octeon_instr_irh *irh; 69162306a36Sopenharmony_ci u32 len; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci iq = oct->instr_queue[sc->iq_no]; 69462306a36Sopenharmony_ci if (!iq->allow_soft_cmds) { 69562306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Soft commands are not allowed on Queue %d\n", 69662306a36Sopenharmony_ci sc->iq_no); 69762306a36Sopenharmony_ci INCR_INSTRQUEUE_PKT_COUNT(oct, sc->iq_no, instr_dropped, 1); 69862306a36Sopenharmony_ci return IQ_SEND_FAILED; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { 70262306a36Sopenharmony_ci ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; 70362306a36Sopenharmony_ci if (ih3->dlengsz) { 70462306a36Sopenharmony_ci WARN_ON(!sc->dmadptr); 70562306a36Sopenharmony_ci sc->cmd.cmd3.dptr = sc->dmadptr; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci irh = (struct octeon_instr_irh *)&sc->cmd.cmd3.irh; 70862306a36Sopenharmony_ci if (irh->rflag) { 70962306a36Sopenharmony_ci WARN_ON(!sc->dmarptr); 71062306a36Sopenharmony_ci WARN_ON(!sc->status_word); 71162306a36Sopenharmony_ci *sc->status_word = COMPLETION_WORD_INIT; 71262306a36Sopenharmony_ci sc->cmd.cmd3.rptr = sc->dmarptr; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci len = (u32)ih3->dlengsz; 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci ih2 = (struct octeon_instr_ih2 *)&sc->cmd.cmd2.ih2; 71762306a36Sopenharmony_ci if (ih2->dlengsz) { 71862306a36Sopenharmony_ci WARN_ON(!sc->dmadptr); 71962306a36Sopenharmony_ci sc->cmd.cmd2.dptr = sc->dmadptr; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci irh = (struct octeon_instr_irh *)&sc->cmd.cmd2.irh; 72262306a36Sopenharmony_ci if (irh->rflag) { 72362306a36Sopenharmony_ci WARN_ON(!sc->dmarptr); 72462306a36Sopenharmony_ci WARN_ON(!sc->status_word); 72562306a36Sopenharmony_ci *sc->status_word = COMPLETION_WORD_INIT; 72662306a36Sopenharmony_ci sc->cmd.cmd2.rptr = sc->dmarptr; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci len = (u32)ih2->dlengsz; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci sc->expiry_time = jiffies + msecs_to_jiffies(LIO_SC_MAX_TMO_MS); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return (octeon_send_command(oct, sc->iq_no, 1, &sc->cmd, sc, 73462306a36Sopenharmony_ci len, REQTYPE_SOFT_COMMAND)); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_send_soft_command); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ciint octeon_setup_sc_buffer_pool(struct octeon_device *oct) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci int i; 74162306a36Sopenharmony_ci u64 dma_addr; 74262306a36Sopenharmony_ci struct octeon_soft_command *sc; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci INIT_LIST_HEAD(&oct->sc_buf_pool.head); 74562306a36Sopenharmony_ci spin_lock_init(&oct->sc_buf_pool.lock); 74662306a36Sopenharmony_ci atomic_set(&oct->sc_buf_pool.alloc_buf_count, 0); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci for (i = 0; i < MAX_SOFT_COMMAND_BUFFERS; i++) { 74962306a36Sopenharmony_ci sc = (struct octeon_soft_command *) 75062306a36Sopenharmony_ci lio_dma_alloc(oct, 75162306a36Sopenharmony_ci SOFT_COMMAND_BUFFER_SIZE, 75262306a36Sopenharmony_ci (dma_addr_t *)&dma_addr); 75362306a36Sopenharmony_ci if (!sc) { 75462306a36Sopenharmony_ci octeon_free_sc_buffer_pool(oct); 75562306a36Sopenharmony_ci return 1; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci sc->dma_addr = dma_addr; 75962306a36Sopenharmony_ci sc->size = SOFT_COMMAND_BUFFER_SIZE; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci list_add_tail(&sc->node, &oct->sc_buf_pool.head); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_setup_sc_buffer_pool); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciint octeon_free_sc_done_list(struct octeon_device *oct) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct octeon_response_list *done_sc_list, *zombie_sc_list; 77162306a36Sopenharmony_ci struct octeon_soft_command *sc; 77262306a36Sopenharmony_ci struct list_head *tmp, *tmp2; 77362306a36Sopenharmony_ci spinlock_t *sc_lists_lock; /* lock for response_list */ 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci done_sc_list = &oct->response_list[OCTEON_DONE_SC_LIST]; 77662306a36Sopenharmony_ci zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST]; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (!atomic_read(&done_sc_list->pending_req_count)) 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci spin_lock_bh(sc_lists_lock); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci list_for_each_safe(tmp, tmp2, &done_sc_list->head) { 78662306a36Sopenharmony_ci sc = list_entry(tmp, struct octeon_soft_command, node); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (READ_ONCE(sc->caller_is_done)) { 78962306a36Sopenharmony_ci list_del(&sc->node); 79062306a36Sopenharmony_ci atomic_dec(&done_sc_list->pending_req_count); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (*sc->status_word == COMPLETION_WORD_INIT) { 79362306a36Sopenharmony_ci /* timeout; move sc to zombie list */ 79462306a36Sopenharmony_ci list_add_tail(&sc->node, &zombie_sc_list->head); 79562306a36Sopenharmony_ci atomic_inc(&zombie_sc_list->pending_req_count); 79662306a36Sopenharmony_ci } else { 79762306a36Sopenharmony_ci octeon_free_soft_command(oct, sc); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci spin_unlock_bh(sc_lists_lock); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_done_list); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ciint octeon_free_sc_zombie_list(struct octeon_device *oct) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct octeon_response_list *zombie_sc_list; 81162306a36Sopenharmony_ci struct octeon_soft_command *sc; 81262306a36Sopenharmony_ci struct list_head *tmp, *tmp2; 81362306a36Sopenharmony_ci spinlock_t *sc_lists_lock; /* lock for response_list */ 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci zombie_sc_list = &oct->response_list[OCTEON_ZOMBIE_SC_LIST]; 81662306a36Sopenharmony_ci sc_lists_lock = &oct->response_list[OCTEON_ORDERED_SC_LIST].lock; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci spin_lock_bh(sc_lists_lock); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci list_for_each_safe(tmp, tmp2, &zombie_sc_list->head) { 82162306a36Sopenharmony_ci list_del(tmp); 82262306a36Sopenharmony_ci atomic_dec(&zombie_sc_list->pending_req_count); 82362306a36Sopenharmony_ci sc = list_entry(tmp, struct octeon_soft_command, node); 82462306a36Sopenharmony_ci octeon_free_soft_command(oct, sc); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci spin_unlock_bh(sc_lists_lock); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_zombie_list); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ciint octeon_free_sc_buffer_pool(struct octeon_device *oct) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct list_head *tmp, *tmp2; 83662306a36Sopenharmony_ci struct octeon_soft_command *sc; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci octeon_free_sc_zombie_list(oct); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci spin_lock_bh(&oct->sc_buf_pool.lock); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci list_for_each_safe(tmp, tmp2, &oct->sc_buf_pool.head) { 84362306a36Sopenharmony_ci list_del(tmp); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci sc = (struct octeon_soft_command *)tmp; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci lio_dma_free(oct, sc->size, sc, sc->dma_addr); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci INIT_LIST_HEAD(&oct->sc_buf_pool.head); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci spin_unlock_bh(&oct->sc_buf_pool.lock); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_sc_buffer_pool); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistruct octeon_soft_command *octeon_alloc_soft_command(struct octeon_device *oct, 85962306a36Sopenharmony_ci u32 datasize, 86062306a36Sopenharmony_ci u32 rdatasize, 86162306a36Sopenharmony_ci u32 ctxsize) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci u64 dma_addr; 86462306a36Sopenharmony_ci u32 size; 86562306a36Sopenharmony_ci u32 offset = sizeof(struct octeon_soft_command); 86662306a36Sopenharmony_ci struct octeon_soft_command *sc = NULL; 86762306a36Sopenharmony_ci struct list_head *tmp; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (!rdatasize) 87062306a36Sopenharmony_ci rdatasize = 16; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci WARN_ON((offset + datasize + rdatasize + ctxsize) > 87362306a36Sopenharmony_ci SOFT_COMMAND_BUFFER_SIZE); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci spin_lock_bh(&oct->sc_buf_pool.lock); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (list_empty(&oct->sc_buf_pool.head)) { 87862306a36Sopenharmony_ci spin_unlock_bh(&oct->sc_buf_pool.lock); 87962306a36Sopenharmony_ci return NULL; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci list_for_each(tmp, &oct->sc_buf_pool.head) 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci list_del(tmp); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci atomic_inc(&oct->sc_buf_pool.alloc_buf_count); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci spin_unlock_bh(&oct->sc_buf_pool.lock); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci sc = (struct octeon_soft_command *)tmp; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci dma_addr = sc->dma_addr; 89462306a36Sopenharmony_ci size = sc->size; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci memset(sc, 0, sc->size); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci sc->dma_addr = dma_addr; 89962306a36Sopenharmony_ci sc->size = size; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (ctxsize) { 90262306a36Sopenharmony_ci sc->ctxptr = (u8 *)sc + offset; 90362306a36Sopenharmony_ci sc->ctxsize = ctxsize; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Start data at 128 byte boundary */ 90762306a36Sopenharmony_ci offset = (offset + ctxsize + 127) & 0xffffff80; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (datasize) { 91062306a36Sopenharmony_ci sc->virtdptr = (u8 *)sc + offset; 91162306a36Sopenharmony_ci sc->dmadptr = dma_addr + offset; 91262306a36Sopenharmony_ci sc->datasize = datasize; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Start rdata at 128 byte boundary */ 91662306a36Sopenharmony_ci offset = (offset + datasize + 127) & 0xffffff80; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (rdatasize) { 91962306a36Sopenharmony_ci WARN_ON(rdatasize < 16); 92062306a36Sopenharmony_ci sc->virtrptr = (u8 *)sc + offset; 92162306a36Sopenharmony_ci sc->dmarptr = dma_addr + offset; 92262306a36Sopenharmony_ci sc->rdatasize = rdatasize; 92362306a36Sopenharmony_ci sc->status_word = (u64 *)((u8 *)(sc->virtrptr) + rdatasize - 8); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return sc; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_alloc_soft_command); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_civoid octeon_free_soft_command(struct octeon_device *oct, 93162306a36Sopenharmony_ci struct octeon_soft_command *sc) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci spin_lock_bh(&oct->sc_buf_pool.lock); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci list_add_tail(&sc->node, &oct->sc_buf_pool.head); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci atomic_dec(&oct->sc_buf_pool.alloc_buf_count); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci spin_unlock_bh(&oct->sc_buf_pool.lock); 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_free_soft_command); 942