18c2ecf20Sopenharmony_ci/********************************************************************** 28c2ecf20Sopenharmony_ci * Author: Cavium, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Contact: support@cavium.com 58c2ecf20Sopenharmony_ci * Please include "LiquidIO" in the subject. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 158c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 168c2ecf20Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more 178c2ecf20Sopenharmony_ci * details. 188c2ecf20Sopenharmony_ci **********************************************************************/ 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 218c2ecf20Sopenharmony_ci#include "liquidio_common.h" 228c2ecf20Sopenharmony_ci#include "octeon_droq.h" 238c2ecf20Sopenharmony_ci#include "octeon_iq.h" 248c2ecf20Sopenharmony_ci#include "response_manager.h" 258c2ecf20Sopenharmony_ci#include "octeon_device.h" 268c2ecf20Sopenharmony_ci#include "octeon_main.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void oct_poll_req_completion(struct work_struct *work); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint octeon_setup_response_list(struct octeon_device *oct) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int i, ret = 0; 338c2ecf20Sopenharmony_ci struct cavium_wq *cwq; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (i = 0; i < MAX_RESPONSE_LISTS; i++) { 368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&oct->response_list[i].head); 378c2ecf20Sopenharmony_ci spin_lock_init(&oct->response_list[i].lock); 388c2ecf20Sopenharmony_ci atomic_set(&oct->response_list[i].pending_req_count, 0); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci spin_lock_init(&oct->cmd_resp_wqlock); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci oct->dma_comp_wq.wq = alloc_workqueue("dma-comp", WQ_MEM_RECLAIM, 0); 438c2ecf20Sopenharmony_ci if (!oct->dma_comp_wq.wq) { 448c2ecf20Sopenharmony_ci dev_err(&oct->pci_dev->dev, "failed to create wq thread\n"); 458c2ecf20Sopenharmony_ci return -ENOMEM; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci cwq = &oct->dma_comp_wq; 498c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&cwq->wk.work, oct_poll_req_completion); 508c2ecf20Sopenharmony_ci cwq->wk.ctxptr = oct; 518c2ecf20Sopenharmony_ci oct->cmd_resp_state = OCT_DRV_ONLINE; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_civoid octeon_delete_response_list(struct octeon_device *oct) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&oct->dma_comp_wq.wk.work); 598c2ecf20Sopenharmony_ci destroy_workqueue(oct->dma_comp_wq.wq); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciint lio_process_ordered_list(struct octeon_device *octeon_dev, 638c2ecf20Sopenharmony_ci u32 force_quit) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct octeon_response_list *ordered_sc_list; 668c2ecf20Sopenharmony_ci struct octeon_soft_command *sc; 678c2ecf20Sopenharmony_ci int request_complete = 0; 688c2ecf20Sopenharmony_ci int resp_to_process = MAX_ORD_REQS_TO_PROCESS; 698c2ecf20Sopenharmony_ci u32 status; 708c2ecf20Sopenharmony_ci u64 status64; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci octeon_free_sc_done_list(octeon_dev); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ordered_sc_list = &octeon_dev->response_list[OCTEON_ORDERED_SC_LIST]; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci spin_lock_bh(&ordered_sc_list->lock); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (list_empty(&ordered_sc_list->head)) { 808c2ecf20Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 818c2ecf20Sopenharmony_ci return 1; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci sc = list_first_entry(&ordered_sc_list->head, 858c2ecf20Sopenharmony_ci struct octeon_soft_command, node); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci status = OCTEON_REQUEST_PENDING; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* check if octeon has finished DMA'ing a response 908c2ecf20Sopenharmony_ci * to where rptr is pointing to 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci status64 = *sc->status_word; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (status64 != COMPLETION_WORD_INIT) { 958c2ecf20Sopenharmony_ci /* This logic ensures that all 64b have been written. 968c2ecf20Sopenharmony_ci * 1. check byte 0 for non-FF 978c2ecf20Sopenharmony_ci * 2. if non-FF, then swap result from BE to host order 988c2ecf20Sopenharmony_ci * 3. check byte 7 (swapped to 0) for non-FF 998c2ecf20Sopenharmony_ci * 4. if non-FF, use the low 32-bit status code 1008c2ecf20Sopenharmony_ci * 5. if either byte 0 or byte 7 is FF, don't use status 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci if ((status64 & 0xff) != 0xff) { 1038c2ecf20Sopenharmony_ci octeon_swap_8B_data(&status64, 1); 1048c2ecf20Sopenharmony_ci if (((status64 & 0xff) != 0xff)) { 1058c2ecf20Sopenharmony_ci /* retrieve 16-bit firmware status */ 1068c2ecf20Sopenharmony_ci status = (u32)(status64 & 0xffffULL); 1078c2ecf20Sopenharmony_ci if (status) { 1088c2ecf20Sopenharmony_ci status = 1098c2ecf20Sopenharmony_ci FIRMWARE_STATUS_CODE(status); 1108c2ecf20Sopenharmony_ci } else { 1118c2ecf20Sopenharmony_ci /* i.e. no error */ 1128c2ecf20Sopenharmony_ci status = OCTEON_REQUEST_DONE; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci } else if (unlikely(force_quit) || (sc->expiry_time && 1178c2ecf20Sopenharmony_ci time_after(jiffies, (unsigned long)sc->expiry_time))) { 1188c2ecf20Sopenharmony_ci struct octeon_instr_irh *irh = 1198c2ecf20Sopenharmony_ci (struct octeon_instr_irh *)&sc->cmd.cmd3.irh; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, "%s: ", __func__); 1228c2ecf20Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, 1238c2ecf20Sopenharmony_ci "cmd %x/%x/%llx/%llx failed, ", 1248c2ecf20Sopenharmony_ci irh->opcode, irh->subcode, 1258c2ecf20Sopenharmony_ci sc->cmd.cmd3.ossp[0], sc->cmd.cmd3.ossp[1]); 1268c2ecf20Sopenharmony_ci dev_err(&octeon_dev->pci_dev->dev, 1278c2ecf20Sopenharmony_ci "timeout (%ld, %ld)\n", 1288c2ecf20Sopenharmony_ci (long)jiffies, (long)sc->expiry_time); 1298c2ecf20Sopenharmony_ci status = OCTEON_REQUEST_TIMEOUT; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (status != OCTEON_REQUEST_PENDING) { 1338c2ecf20Sopenharmony_ci sc->sc_status = status; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* we have received a response or we have timed out */ 1368c2ecf20Sopenharmony_ci /* remove node from linked list */ 1378c2ecf20Sopenharmony_ci list_del(&sc->node); 1388c2ecf20Sopenharmony_ci atomic_dec(&octeon_dev->response_list 1398c2ecf20Sopenharmony_ci [OCTEON_ORDERED_SC_LIST]. 1408c2ecf20Sopenharmony_ci pending_req_count); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!sc->callback) { 1438c2ecf20Sopenharmony_ci atomic_inc(&octeon_dev->response_list 1448c2ecf20Sopenharmony_ci [OCTEON_DONE_SC_LIST]. 1458c2ecf20Sopenharmony_ci pending_req_count); 1468c2ecf20Sopenharmony_ci list_add_tail(&sc->node, 1478c2ecf20Sopenharmony_ci &octeon_dev->response_list 1488c2ecf20Sopenharmony_ci [OCTEON_DONE_SC_LIST].head); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (unlikely(READ_ONCE(sc->caller_is_done))) { 1518c2ecf20Sopenharmony_ci /* caller does not wait for response 1528c2ecf20Sopenharmony_ci * from firmware 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (status != OCTEON_REQUEST_DONE) { 1558c2ecf20Sopenharmony_ci struct octeon_instr_irh *irh; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci irh = 1588c2ecf20Sopenharmony_ci (struct octeon_instr_irh *) 1598c2ecf20Sopenharmony_ci &sc->cmd.cmd3.irh; 1608c2ecf20Sopenharmony_ci dev_dbg 1618c2ecf20Sopenharmony_ci (&octeon_dev->pci_dev->dev, 1628c2ecf20Sopenharmony_ci "%s: sc failed: opcode=%x, ", 1638c2ecf20Sopenharmony_ci __func__, irh->opcode); 1648c2ecf20Sopenharmony_ci dev_dbg 1658c2ecf20Sopenharmony_ci (&octeon_dev->pci_dev->dev, 1668c2ecf20Sopenharmony_ci "subcode=%x, ossp[0]=%llx, ", 1678c2ecf20Sopenharmony_ci irh->subcode, 1688c2ecf20Sopenharmony_ci sc->cmd.cmd3.ossp[0]); 1698c2ecf20Sopenharmony_ci dev_dbg 1708c2ecf20Sopenharmony_ci (&octeon_dev->pci_dev->dev, 1718c2ecf20Sopenharmony_ci "ossp[1]=%llx, status=%d\n", 1728c2ecf20Sopenharmony_ci sc->cmd.cmd3.ossp[1], 1738c2ecf20Sopenharmony_ci status); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci complete(&sc->complete); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci /* sc with callback function */ 1828c2ecf20Sopenharmony_ci if (status == OCTEON_REQUEST_TIMEOUT) { 1838c2ecf20Sopenharmony_ci atomic_inc(&octeon_dev->response_list 1848c2ecf20Sopenharmony_ci [OCTEON_ZOMBIE_SC_LIST]. 1858c2ecf20Sopenharmony_ci pending_req_count); 1868c2ecf20Sopenharmony_ci list_add_tail(&sc->node, 1878c2ecf20Sopenharmony_ci &octeon_dev->response_list 1888c2ecf20Sopenharmony_ci [OCTEON_ZOMBIE_SC_LIST]. 1898c2ecf20Sopenharmony_ci head); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci spin_unlock_bh(&ordered_sc_list->lock); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci sc->callback(octeon_dev, status, 1958c2ecf20Sopenharmony_ci sc->callback_arg); 1968c2ecf20Sopenharmony_ci /* sc is freed by caller */ 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci request_complete++; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci /* no response yet */ 2038c2ecf20Sopenharmony_ci request_complete = 0; 2048c2ecf20Sopenharmony_ci spin_unlock_bh 2058c2ecf20Sopenharmony_ci (&ordered_sc_list->lock); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* If we hit the Max Ordered requests to process every loop, 2098c2ecf20Sopenharmony_ci * we quit 2108c2ecf20Sopenharmony_ci * and let this function be invoked the next time the poll 2118c2ecf20Sopenharmony_ci * thread runs 2128c2ecf20Sopenharmony_ci * to process the remaining requests. This function can take up 2138c2ecf20Sopenharmony_ci * the entire CPU if there is no upper limit to the requests 2148c2ecf20Sopenharmony_ci * processed. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci if (request_complete >= resp_to_process) 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci } while (request_complete); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void oct_poll_req_completion(struct work_struct *work) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct cavium_wk *wk = (struct cavium_wk *)work; 2268c2ecf20Sopenharmony_ci struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; 2278c2ecf20Sopenharmony_ci struct cavium_wq *cwq = &oct->dma_comp_wq; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci lio_process_ordered_list(oct, 0); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (atomic_read(&oct->response_list 2328c2ecf20Sopenharmony_ci [OCTEON_ORDERED_SC_LIST].pending_req_count)) 2338c2ecf20Sopenharmony_ci queue_delayed_work(cwq->wq, &cwq->wk.work, msecs_to_jiffies(1)); 2348c2ecf20Sopenharmony_ci} 235