18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify 58c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 68c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 98c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 118c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 128c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 148c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 158c2ecf20Sopenharmony_ci * SOFTWARE. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 248c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 258c2ecf20Sopenharmony_ci#include <linux/mempool.h> 268c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 278c2ecf20Sopenharmony_ci#include <linux/ctype.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "snic_io.h" 308c2ecf20Sopenharmony_ci#include "snic.h" 318c2ecf20Sopenharmony_ci#include "cq_enet_desc.h" 328c2ecf20Sopenharmony_ci#include "snic_fwint.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * snic_handle_link : Handles link flaps. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_civoid 388c2ecf20Sopenharmony_cisnic_handle_link(struct work_struct *work) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct snic *snic = container_of(work, struct snic, link_work); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (snic->config.xpt_type == SNIC_DAS) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci snic->link_status = svnic_dev_link_status(snic->vdev); 468c2ecf20Sopenharmony_ci snic->link_down_cnt = svnic_dev_link_down_cnt(snic->vdev); 478c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Link Event: Link %s.\n", 488c2ecf20Sopenharmony_ci ((snic->link_status) ? "Up" : "Down")); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci SNIC_ASSERT_NOT_IMPL(1); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * snic_ver_enc : Encodes version str to int 568c2ecf20Sopenharmony_ci * version string is similar to netmask string 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic int 598c2ecf20Sopenharmony_cisnic_ver_enc(const char *s) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int v[4] = {0}; 628c2ecf20Sopenharmony_ci int i = 0, x = 0; 638c2ecf20Sopenharmony_ci char c; 648c2ecf20Sopenharmony_ci const char *p = s; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* validate version string */ 678c2ecf20Sopenharmony_ci if ((strlen(s) > 15) || (strlen(s) < 7)) 688c2ecf20Sopenharmony_ci goto end; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci while ((c = *p++)) { 718c2ecf20Sopenharmony_ci if (c == '.') { 728c2ecf20Sopenharmony_ci i++; 738c2ecf20Sopenharmony_ci continue; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (i > 3 || !isdigit(c)) 778c2ecf20Sopenharmony_ci goto end; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci v[i] = v[i] * 10 + (c - '0'); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* validate sub version numbers */ 838c2ecf20Sopenharmony_ci for (i = 3; i >= 0; i--) 848c2ecf20Sopenharmony_ci if (v[i] > 0xff) 858c2ecf20Sopenharmony_ci goto end; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci x |= (v[0] << 24) | v[1] << 16 | v[2] << 8 | v[3]; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciend: 908c2ecf20Sopenharmony_ci if (x == 0) { 918c2ecf20Sopenharmony_ci SNIC_ERR("Invalid version string [%s].\n", s); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return -1; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return x; 978c2ecf20Sopenharmony_ci} /* end of snic_ver_enc */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * snic_qeueue_exch_ver_req : 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Queues Exchange Version Request, to communicate host information 1038c2ecf20Sopenharmony_ci * in return, it gets firmware version details 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ciint 1068c2ecf20Sopenharmony_cisnic_queue_exch_ver_req(struct snic *snic) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct snic_req_info *rqi = NULL; 1098c2ecf20Sopenharmony_ci struct snic_host_req *req = NULL; 1108c2ecf20Sopenharmony_ci u32 ver = 0; 1118c2ecf20Sopenharmony_ci int ret = 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Exch Ver Req Preparing...\n"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci rqi = snic_req_init(snic, 0); 1168c2ecf20Sopenharmony_ci if (!rqi) { 1178c2ecf20Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 1188c2ecf20Sopenharmony_ci "Queuing Exch Ver Req failed, err = %d\n", 1198c2ecf20Sopenharmony_ci ret); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = -ENOMEM; 1228c2ecf20Sopenharmony_ci goto error; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci req = rqi_to_req(rqi); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Initialize snic_host_req */ 1288c2ecf20Sopenharmony_ci snic_io_hdr_enc(&req->hdr, SNIC_REQ_EXCH_VER, 0, SCSI_NO_TAG, 1298c2ecf20Sopenharmony_ci snic->config.hid, 0, (ulong)rqi); 1308c2ecf20Sopenharmony_ci ver = snic_ver_enc(SNIC_DRV_VERSION); 1318c2ecf20Sopenharmony_ci req->u.exch_ver.drvr_ver = cpu_to_le32(ver); 1328c2ecf20Sopenharmony_ci req->u.exch_ver.os_type = cpu_to_le32(SNIC_OS_LINUX); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci snic_handle_untagged_req(snic, rqi); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = snic_queue_wq_desc(snic, req, sizeof(*req)); 1378c2ecf20Sopenharmony_ci if (ret) { 1388c2ecf20Sopenharmony_ci snic_release_untagged_req(snic, rqi); 1398c2ecf20Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 1408c2ecf20Sopenharmony_ci "Queuing Exch Ver Req failed, err = %d\n", 1418c2ecf20Sopenharmony_ci ret); 1428c2ecf20Sopenharmony_ci goto error; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Exch Ver Req is issued. ret = %d\n", ret); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cierror: 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci} /* end of snic_queue_exch_ver_req */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * snic_io_exch_ver_cmpl_handler 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_civoid 1558c2ecf20Sopenharmony_cisnic_io_exch_ver_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct snic_req_info *rqi = NULL; 1588c2ecf20Sopenharmony_ci struct snic_exch_ver_rsp *exv_cmpl = &fwreq->u.exch_ver_cmpl; 1598c2ecf20Sopenharmony_ci u8 typ, hdr_stat; 1608c2ecf20Sopenharmony_ci u32 cmnd_id, hid, max_sgs; 1618c2ecf20Sopenharmony_ci ulong ctx = 0; 1628c2ecf20Sopenharmony_ci unsigned long flags; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Exch Ver Compl Received.\n"); 1658c2ecf20Sopenharmony_ci snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); 1668c2ecf20Sopenharmony_ci SNIC_BUG_ON(snic->config.hid != hid); 1678c2ecf20Sopenharmony_ci rqi = (struct snic_req_info *) ctx; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (hdr_stat) { 1708c2ecf20Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 1718c2ecf20Sopenharmony_ci "Exch Ver Completed w/ err status %d\n", 1728c2ecf20Sopenharmony_ci hdr_stat); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci goto exch_cmpl_end; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 1788c2ecf20Sopenharmony_ci snic->fwinfo.fw_ver = le32_to_cpu(exv_cmpl->version); 1798c2ecf20Sopenharmony_ci snic->fwinfo.hid = le32_to_cpu(exv_cmpl->hid); 1808c2ecf20Sopenharmony_ci snic->fwinfo.max_concur_ios = le32_to_cpu(exv_cmpl->max_concur_ios); 1818c2ecf20Sopenharmony_ci snic->fwinfo.max_sgs_per_cmd = le32_to_cpu(exv_cmpl->max_sgs_per_cmd); 1828c2ecf20Sopenharmony_ci snic->fwinfo.max_io_sz = le32_to_cpu(exv_cmpl->max_io_sz); 1838c2ecf20Sopenharmony_ci snic->fwinfo.max_tgts = le32_to_cpu(exv_cmpl->max_tgts); 1848c2ecf20Sopenharmony_ci snic->fwinfo.io_tmo = le16_to_cpu(exv_cmpl->io_timeout); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, 1878c2ecf20Sopenharmony_ci "vers %u hid %u max_concur_ios %u max_sgs_per_cmd %u max_io_sz %u max_tgts %u fw tmo %u\n", 1888c2ecf20Sopenharmony_ci snic->fwinfo.fw_ver, 1898c2ecf20Sopenharmony_ci snic->fwinfo.hid, 1908c2ecf20Sopenharmony_ci snic->fwinfo.max_concur_ios, 1918c2ecf20Sopenharmony_ci snic->fwinfo.max_sgs_per_cmd, 1928c2ecf20Sopenharmony_ci snic->fwinfo.max_io_sz, 1938c2ecf20Sopenharmony_ci snic->fwinfo.max_tgts, 1948c2ecf20Sopenharmony_ci snic->fwinfo.io_tmo); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, 1978c2ecf20Sopenharmony_ci "HBA Capabilities = 0x%x\n", 1988c2ecf20Sopenharmony_ci le32_to_cpu(exv_cmpl->hba_cap)); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Updating SGList size */ 2018c2ecf20Sopenharmony_ci max_sgs = snic->fwinfo.max_sgs_per_cmd; 2028c2ecf20Sopenharmony_ci if (max_sgs && max_sgs < SNIC_MAX_SG_DESC_CNT) { 2038c2ecf20Sopenharmony_ci snic->shost->sg_tablesize = max_sgs; 2048c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Max SGs set to %d\n", 2058c2ecf20Sopenharmony_ci snic->shost->sg_tablesize); 2068c2ecf20Sopenharmony_ci } else if (max_sgs > snic->shost->sg_tablesize) { 2078c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, 2088c2ecf20Sopenharmony_ci "Target type %d Supports Larger Max SGList %d than driver's Max SG List %d.\n", 2098c2ecf20Sopenharmony_ci snic->config.xpt_type, max_sgs, 2108c2ecf20Sopenharmony_ci snic->shost->sg_tablesize); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (snic->shost->can_queue > snic->fwinfo.max_concur_ios) 2148c2ecf20Sopenharmony_ci snic->shost->can_queue = snic->fwinfo.max_concur_ios; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci snic->shost->max_sectors = snic->fwinfo.max_io_sz >> 9; 2178c2ecf20Sopenharmony_ci if (snic->fwinfo.wait) 2188c2ecf20Sopenharmony_ci complete(snic->fwinfo.wait); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciexch_cmpl_end: 2238c2ecf20Sopenharmony_ci snic_release_untagged_req(snic, rqi); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Exch_cmpl Done, hdr_stat %d.\n", hdr_stat); 2268c2ecf20Sopenharmony_ci} /* end of snic_io_exch_ver_cmpl_handler */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * snic_get_conf 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * Synchronous call, and Retrieves snic params. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ciint 2348c2ecf20Sopenharmony_cisnic_get_conf(struct snic *snic) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(wait); 2378c2ecf20Sopenharmony_ci unsigned long flags; 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci int nr_retries = 3; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci SNIC_HOST_INFO(snic->shost, "Retrieving snic params.\n"); 2428c2ecf20Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 2438c2ecf20Sopenharmony_ci memset(&snic->fwinfo, 0, sizeof(snic->fwinfo)); 2448c2ecf20Sopenharmony_ci snic->fwinfo.wait = &wait; 2458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Additional delay to handle HW Resource initialization. */ 2488c2ecf20Sopenharmony_ci msleep(50); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * Exch ver req can be ignored by FW, if HW Resource initialization 2528c2ecf20Sopenharmony_ci * is in progress, Hence retry. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci do { 2558c2ecf20Sopenharmony_ci ret = snic_queue_exch_ver_req(snic); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)); 2608c2ecf20Sopenharmony_ci spin_lock_irqsave(&snic->snic_lock, flags); 2618c2ecf20Sopenharmony_ci ret = (snic->fwinfo.fw_ver != 0) ? 0 : -ETIMEDOUT; 2628c2ecf20Sopenharmony_ci if (ret) 2638c2ecf20Sopenharmony_ci SNIC_HOST_ERR(snic->shost, 2648c2ecf20Sopenharmony_ci "Failed to retrieve snic params,\n"); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Unset fwinfo.wait, on success or on last retry */ 2678c2ecf20Sopenharmony_ci if (ret == 0 || nr_retries == 1) 2688c2ecf20Sopenharmony_ci snic->fwinfo.wait = NULL; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&snic->snic_lock, flags); 2718c2ecf20Sopenharmony_ci } while (ret && --nr_retries); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci} /* end of snic_get_info */ 275