1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright 2014 Cisco Systems, Inc. All rights reserved. 3 4 #include <linux/errno.h> 5 #include <linux/pci.h> 6 #include <linux/slab.h> 7 8 #include <linux/interrupt.h> 9 #include <linux/workqueue.h> 10 #include <linux/spinlock.h> 11 #include <linux/mempool.h> 12 #include <scsi/scsi_tcq.h> 13 #include <linux/ctype.h> 14 15 #include "snic_io.h" 16 #include "snic.h" 17 #include "cq_enet_desc.h" 18 #include "snic_fwint.h" 19 20 /* 21 * snic_handle_link : Handles link flaps. 22 */ 23 void 24 snic_handle_link(struct work_struct *work) 25 { 26 struct snic *snic = container_of(work, struct snic, link_work); 27 28 if (snic->config.xpt_type == SNIC_DAS) 29 return; 30 31 snic->link_status = svnic_dev_link_status(snic->vdev); 32 snic->link_down_cnt = svnic_dev_link_down_cnt(snic->vdev); 33 SNIC_HOST_INFO(snic->shost, "Link Event: Link %s.\n", 34 ((snic->link_status) ? "Up" : "Down")); 35 36 SNIC_ASSERT_NOT_IMPL(1); 37 } 38 39 40 /* 41 * snic_ver_enc : Encodes version str to int 42 * version string is similar to netmask string 43 */ 44 static int 45 snic_ver_enc(const char *s) 46 { 47 int v[4] = {0}; 48 int i = 0, x = 0; 49 char c; 50 const char *p = s; 51 52 /* validate version string */ 53 if ((strlen(s) > 15) || (strlen(s) < 7)) 54 goto end; 55 56 while ((c = *p++)) { 57 if (c == '.') { 58 i++; 59 continue; 60 } 61 62 if (i > 3 || !isdigit(c)) 63 goto end; 64 65 v[i] = v[i] * 10 + (c - '0'); 66 } 67 68 /* validate sub version numbers */ 69 for (i = 3; i >= 0; i--) 70 if (v[i] > 0xff) 71 goto end; 72 73 x |= (v[0] << 24) | v[1] << 16 | v[2] << 8 | v[3]; 74 75 end: 76 if (x == 0) { 77 SNIC_ERR("Invalid version string [%s].\n", s); 78 79 return -1; 80 } 81 82 return x; 83 } /* end of snic_ver_enc */ 84 85 /* 86 * snic_qeueue_exch_ver_req : 87 * 88 * Queues Exchange Version Request, to communicate host information 89 * in return, it gets firmware version details 90 */ 91 int 92 snic_queue_exch_ver_req(struct snic *snic) 93 { 94 struct snic_req_info *rqi = NULL; 95 struct snic_host_req *req = NULL; 96 u32 ver = 0; 97 int ret = 0; 98 99 SNIC_HOST_INFO(snic->shost, "Exch Ver Req Preparing...\n"); 100 101 rqi = snic_req_init(snic, 0); 102 if (!rqi) { 103 SNIC_HOST_ERR(snic->shost, "Init Exch Ver Req failed\n"); 104 ret = -ENOMEM; 105 goto error; 106 } 107 108 req = rqi_to_req(rqi); 109 110 /* Initialize snic_host_req */ 111 snic_io_hdr_enc(&req->hdr, SNIC_REQ_EXCH_VER, 0, SCSI_NO_TAG, 112 snic->config.hid, 0, (ulong)rqi); 113 ver = snic_ver_enc(SNIC_DRV_VERSION); 114 req->u.exch_ver.drvr_ver = cpu_to_le32(ver); 115 req->u.exch_ver.os_type = cpu_to_le32(SNIC_OS_LINUX); 116 117 snic_handle_untagged_req(snic, rqi); 118 119 ret = snic_queue_wq_desc(snic, req, sizeof(*req)); 120 if (ret) { 121 snic_release_untagged_req(snic, rqi); 122 SNIC_HOST_ERR(snic->shost, 123 "Queuing Exch Ver Req failed, err = %d\n", 124 ret); 125 goto error; 126 } 127 128 SNIC_HOST_INFO(snic->shost, "Exch Ver Req is issued. ret = %d\n", ret); 129 130 error: 131 return ret; 132 } /* end of snic_queue_exch_ver_req */ 133 134 /* 135 * snic_io_exch_ver_cmpl_handler 136 */ 137 void 138 snic_io_exch_ver_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) 139 { 140 struct snic_req_info *rqi = NULL; 141 struct snic_exch_ver_rsp *exv_cmpl = &fwreq->u.exch_ver_cmpl; 142 u8 typ, hdr_stat; 143 u32 cmnd_id, hid, max_sgs; 144 ulong ctx = 0; 145 unsigned long flags; 146 147 SNIC_HOST_INFO(snic->shost, "Exch Ver Compl Received.\n"); 148 snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); 149 SNIC_BUG_ON(snic->config.hid != hid); 150 rqi = (struct snic_req_info *) ctx; 151 152 if (hdr_stat) { 153 SNIC_HOST_ERR(snic->shost, 154 "Exch Ver Completed w/ err status %d\n", 155 hdr_stat); 156 157 goto exch_cmpl_end; 158 } 159 160 spin_lock_irqsave(&snic->snic_lock, flags); 161 snic->fwinfo.fw_ver = le32_to_cpu(exv_cmpl->version); 162 snic->fwinfo.hid = le32_to_cpu(exv_cmpl->hid); 163 snic->fwinfo.max_concur_ios = le32_to_cpu(exv_cmpl->max_concur_ios); 164 snic->fwinfo.max_sgs_per_cmd = le32_to_cpu(exv_cmpl->max_sgs_per_cmd); 165 snic->fwinfo.max_io_sz = le32_to_cpu(exv_cmpl->max_io_sz); 166 snic->fwinfo.max_tgts = le32_to_cpu(exv_cmpl->max_tgts); 167 snic->fwinfo.io_tmo = le16_to_cpu(exv_cmpl->io_timeout); 168 169 SNIC_HOST_INFO(snic->shost, 170 "vers %u hid %u max_concur_ios %u max_sgs_per_cmd %u max_io_sz %u max_tgts %u fw tmo %u\n", 171 snic->fwinfo.fw_ver, 172 snic->fwinfo.hid, 173 snic->fwinfo.max_concur_ios, 174 snic->fwinfo.max_sgs_per_cmd, 175 snic->fwinfo.max_io_sz, 176 snic->fwinfo.max_tgts, 177 snic->fwinfo.io_tmo); 178 179 SNIC_HOST_INFO(snic->shost, 180 "HBA Capabilities = 0x%x\n", 181 le32_to_cpu(exv_cmpl->hba_cap)); 182 183 /* Updating SGList size */ 184 max_sgs = snic->fwinfo.max_sgs_per_cmd; 185 if (max_sgs && max_sgs < SNIC_MAX_SG_DESC_CNT) { 186 snic->shost->sg_tablesize = max_sgs; 187 SNIC_HOST_INFO(snic->shost, "Max SGs set to %d\n", 188 snic->shost->sg_tablesize); 189 } else if (max_sgs > snic->shost->sg_tablesize) { 190 SNIC_HOST_INFO(snic->shost, 191 "Target type %d Supports Larger Max SGList %d than driver's Max SG List %d.\n", 192 snic->config.xpt_type, max_sgs, 193 snic->shost->sg_tablesize); 194 } 195 196 if (snic->shost->can_queue > snic->fwinfo.max_concur_ios) 197 snic->shost->can_queue = snic->fwinfo.max_concur_ios; 198 199 snic->shost->max_sectors = snic->fwinfo.max_io_sz >> 9; 200 if (snic->fwinfo.wait) 201 complete(snic->fwinfo.wait); 202 203 spin_unlock_irqrestore(&snic->snic_lock, flags); 204 205 exch_cmpl_end: 206 snic_release_untagged_req(snic, rqi); 207 208 SNIC_HOST_INFO(snic->shost, "Exch_cmpl Done, hdr_stat %d.\n", hdr_stat); 209 } /* end of snic_io_exch_ver_cmpl_handler */ 210 211 /* 212 * snic_get_conf 213 * 214 * Synchronous call, and Retrieves snic params. 215 */ 216 int 217 snic_get_conf(struct snic *snic) 218 { 219 DECLARE_COMPLETION_ONSTACK(wait); 220 unsigned long flags; 221 int ret; 222 int nr_retries = 3; 223 224 SNIC_HOST_INFO(snic->shost, "Retrieving snic params.\n"); 225 spin_lock_irqsave(&snic->snic_lock, flags); 226 memset(&snic->fwinfo, 0, sizeof(snic->fwinfo)); 227 snic->fwinfo.wait = &wait; 228 spin_unlock_irqrestore(&snic->snic_lock, flags); 229 230 /* Additional delay to handle HW Resource initialization. */ 231 msleep(50); 232 233 /* 234 * Exch ver req can be ignored by FW, if HW Resource initialization 235 * is in progress, Hence retry. 236 */ 237 do { 238 ret = snic_queue_exch_ver_req(snic); 239 if (ret) 240 return ret; 241 242 wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)); 243 spin_lock_irqsave(&snic->snic_lock, flags); 244 ret = (snic->fwinfo.fw_ver != 0) ? 0 : -ETIMEDOUT; 245 if (ret) 246 SNIC_HOST_ERR(snic->shost, 247 "Failed to retrieve snic params,\n"); 248 249 /* Unset fwinfo.wait, on success or on last retry */ 250 if (ret == 0 || nr_retries == 1) 251 snic->fwinfo.wait = NULL; 252 253 spin_unlock_irqrestore(&snic->snic_lock, flags); 254 } while (ret && --nr_retries); 255 256 return ret; 257 } /* end of snic_get_info */ 258