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