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 "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 2862306a36Sopenharmony_cistatic void oct_poll_req_completion(struct work_struct *work); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciint octeon_setup_response_list(struct octeon_device *oct) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int i, ret = 0; 3362306a36Sopenharmony_ci struct cavium_wq *cwq; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci for (i = 0; i < MAX_RESPONSE_LISTS; i++) { 3662306a36Sopenharmony_ci INIT_LIST_HEAD(&oct->response_list[i].head); 3762306a36Sopenharmony_ci spin_lock_init(&oct->response_list[i].lock); 3862306a36Sopenharmony_ci atomic_set(&oct->response_list[i].pending_req_count, 0); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci spin_lock_init(&oct->cmd_resp_wqlock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci oct->dma_comp_wq.wq = alloc_workqueue("dma-comp", WQ_MEM_RECLAIM, 0); 4362306a36Sopenharmony_ci if (!oct->dma_comp_wq.wq) { 4462306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "failed to create wq thread\n"); 4562306a36Sopenharmony_ci return -ENOMEM; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci cwq = &oct->dma_comp_wq; 4962306a36Sopenharmony_ci INIT_DELAYED_WORK(&cwq->wk.work, oct_poll_req_completion); 5062306a36Sopenharmony_ci cwq->wk.ctxptr = oct; 5162306a36Sopenharmony_ci oct->cmd_resp_state = OCT_DRV_ONLINE; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return ret; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_setup_response_list); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid octeon_delete_response_list(struct octeon_device *oct) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci cancel_delayed_work_sync(&oct->dma_comp_wq.wk.work); 6062306a36Sopenharmony_ci destroy_workqueue(oct->dma_comp_wq.wq); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(octeon_delete_response_list); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint lio_process_ordered_list(struct octeon_device *octeon_dev, 6562306a36Sopenharmony_ci u32 force_quit) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct octeon_response_list *ordered_sc_list; 6862306a36Sopenharmony_ci struct octeon_soft_command *sc; 6962306a36Sopenharmony_ci int request_complete = 0; 7062306a36Sopenharmony_ci int resp_to_process = MAX_ORD_REQS_TO_PROCESS; 7162306a36Sopenharmony_ci u32 status; 7262306a36Sopenharmony_ci u64 status64; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci octeon_free_sc_done_list(octeon_dev); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ordered_sc_list = &octeon_dev->response_list[OCTEON_ORDERED_SC_LIST]; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci do { 7962306a36Sopenharmony_ci spin_lock_bh(&ordered_sc_list->lock); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (list_empty(&ordered_sc_list->head)) { 8262306a36Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 8362306a36Sopenharmony_ci return 1; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci sc = list_first_entry(&ordered_sc_list->head, 8762306a36Sopenharmony_ci struct octeon_soft_command, node); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci status = OCTEON_REQUEST_PENDING; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check if octeon has finished DMA'ing a response 9262306a36Sopenharmony_ci * to where rptr is pointing to 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci status64 = *sc->status_word; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (status64 != COMPLETION_WORD_INIT) { 9762306a36Sopenharmony_ci /* This logic ensures that all 64b have been written. 9862306a36Sopenharmony_ci * 1. check byte 0 for non-FF 9962306a36Sopenharmony_ci * 2. if non-FF, then swap result from BE to host order 10062306a36Sopenharmony_ci * 3. check byte 7 (swapped to 0) for non-FF 10162306a36Sopenharmony_ci * 4. if non-FF, use the low 32-bit status code 10262306a36Sopenharmony_ci * 5. if either byte 0 or byte 7 is FF, don't use status 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci if ((status64 & 0xff) != 0xff) { 10562306a36Sopenharmony_ci octeon_swap_8B_data(&status64, 1); 10662306a36Sopenharmony_ci if (((status64 & 0xff) != 0xff)) { 10762306a36Sopenharmony_ci /* retrieve 16-bit firmware status */ 10862306a36Sopenharmony_ci status = (u32)(status64 & 0xffffULL); 10962306a36Sopenharmony_ci if (status) { 11062306a36Sopenharmony_ci status = 11162306a36Sopenharmony_ci FIRMWARE_STATUS_CODE(status); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci /* i.e. no error */ 11462306a36Sopenharmony_ci status = OCTEON_REQUEST_DONE; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } else if (unlikely(force_quit) || (sc->expiry_time && 11962306a36Sopenharmony_ci time_after(jiffies, (unsigned long)sc->expiry_time))) { 12062306a36Sopenharmony_ci struct octeon_instr_irh *irh = 12162306a36Sopenharmony_ci (struct octeon_instr_irh *)&sc->cmd.cmd3.irh; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, "%s: ", __func__); 12462306a36Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, 12562306a36Sopenharmony_ci "cmd %x/%x/%llx/%llx failed, ", 12662306a36Sopenharmony_ci irh->opcode, irh->subcode, 12762306a36Sopenharmony_ci sc->cmd.cmd3.ossp[0], sc->cmd.cmd3.ossp[1]); 12862306a36Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, 12962306a36Sopenharmony_ci "timeout (%ld, %ld)\n", 13062306a36Sopenharmony_ci (long)jiffies, (long)sc->expiry_time); 13162306a36Sopenharmony_ci status = OCTEON_REQUEST_TIMEOUT; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (status != OCTEON_REQUEST_PENDING) { 13562306a36Sopenharmony_ci sc->sc_status = status; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* we have received a response or we have timed out */ 13862306a36Sopenharmony_ci /* remove node from linked list */ 13962306a36Sopenharmony_ci list_del(&sc->node); 14062306a36Sopenharmony_ci atomic_dec(&octeon_dev->response_list 14162306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST]. 14262306a36Sopenharmony_ci pending_req_count); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!sc->callback) { 14562306a36Sopenharmony_ci atomic_inc(&octeon_dev->response_list 14662306a36Sopenharmony_ci [OCTEON_DONE_SC_LIST]. 14762306a36Sopenharmony_ci pending_req_count); 14862306a36Sopenharmony_ci list_add_tail(&sc->node, 14962306a36Sopenharmony_ci &octeon_dev->response_list 15062306a36Sopenharmony_ci [OCTEON_DONE_SC_LIST].head); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (unlikely(READ_ONCE(sc->caller_is_done))) { 15362306a36Sopenharmony_ci /* caller does not wait for response 15462306a36Sopenharmony_ci * from firmware 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci if (status != OCTEON_REQUEST_DONE) { 15762306a36Sopenharmony_ci struct octeon_instr_irh *irh; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci irh = 16062306a36Sopenharmony_ci (struct octeon_instr_irh *) 16162306a36Sopenharmony_ci &sc->cmd.cmd3.irh; 16262306a36Sopenharmony_ci dev_dbg 16362306a36Sopenharmony_ci (&octeon_dev->pci_dev->dev, 16462306a36Sopenharmony_ci "%s: sc failed: opcode=%x, ", 16562306a36Sopenharmony_ci __func__, irh->opcode); 16662306a36Sopenharmony_ci dev_dbg 16762306a36Sopenharmony_ci (&octeon_dev->pci_dev->dev, 16862306a36Sopenharmony_ci "subcode=%x, ossp[0]=%llx, ", 16962306a36Sopenharmony_ci irh->subcode, 17062306a36Sopenharmony_ci sc->cmd.cmd3.ossp[0]); 17162306a36Sopenharmony_ci dev_dbg 17262306a36Sopenharmony_ci (&octeon_dev->pci_dev->dev, 17362306a36Sopenharmony_ci "ossp[1]=%llx, status=%d\n", 17462306a36Sopenharmony_ci sc->cmd.cmd3.ossp[1], 17562306a36Sopenharmony_ci status); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci complete(&sc->complete); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci /* sc with callback function */ 18462306a36Sopenharmony_ci if (status == OCTEON_REQUEST_TIMEOUT) { 18562306a36Sopenharmony_ci atomic_inc(&octeon_dev->response_list 18662306a36Sopenharmony_ci [OCTEON_ZOMBIE_SC_LIST]. 18762306a36Sopenharmony_ci pending_req_count); 18862306a36Sopenharmony_ci list_add_tail(&sc->node, 18962306a36Sopenharmony_ci &octeon_dev->response_list 19062306a36Sopenharmony_ci [OCTEON_ZOMBIE_SC_LIST]. 19162306a36Sopenharmony_ci head); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci sc->callback(octeon_dev, status, 19762306a36Sopenharmony_ci sc->callback_arg); 19862306a36Sopenharmony_ci /* sc is freed by caller */ 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci request_complete++; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci /* no response yet */ 20562306a36Sopenharmony_ci request_complete = 0; 20662306a36Sopenharmony_ci spin_unlock_bh 20762306a36Sopenharmony_ci (&ordered_sc_list->lock); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* If we hit the Max Ordered requests to process every loop, 21162306a36Sopenharmony_ci * we quit 21262306a36Sopenharmony_ci * and let this function be invoked the next time the poll 21362306a36Sopenharmony_ci * thread runs 21462306a36Sopenharmony_ci * to process the remaining requests. This function can take up 21562306a36Sopenharmony_ci * the entire CPU if there is no upper limit to the requests 21662306a36Sopenharmony_ci * processed. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci if (request_complete >= resp_to_process) 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci } while (request_complete); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lio_process_ordered_list); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void oct_poll_req_completion(struct work_struct *work) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct cavium_wk *wk = (struct cavium_wk *)work; 22962306a36Sopenharmony_ci struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; 23062306a36Sopenharmony_ci struct cavium_wq *cwq = &oct->dma_comp_wq; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci lio_process_ordered_list(oct, 0); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (atomic_read(&oct->response_list 23562306a36Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].pending_req_count)) 23662306a36Sopenharmony_ci queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(1)); 23762306a36Sopenharmony_ci} 238