xref: /openbmc/linux/drivers/scsi/snic/snic_ctl.c (revision dfb99b05)
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
snic_handle_link(struct work_struct * work)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
snic_ver_enc(const char * s)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
snic_queue_exch_ver_req(struct snic * snic)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
snic_io_exch_ver_cmpl_handler(struct snic * snic,struct snic_fw_req * fwreq)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
snic_get_conf(struct snic * snic)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