Commit | Line | Data |
---|---|---|
dfb99b05 TG |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // Copyright 2014 Cisco Systems, Inc. All rights reserved. | |
c8806b6c NM |
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 | ||
3f5c11a4 | 28 | if (snic->config.xpt_type == SNIC_DAS) |
c8806b6c | 29 | return; |
c8806b6c NM |
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")); | |
3f5c11a4 NM |
35 | |
36 | SNIC_ASSERT_NOT_IMPL(1); | |
c8806b6c NM |
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 | ||
bbb7bace | 62 | if (i > 3 || !isdigit(c)) |
c8806b6c NM |
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) { | |
9959d451 | 103 | SNIC_HOST_ERR(snic->shost, "Init Exch Ver Req failed\n"); |
c8806b6c NM |
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 | */ | |
6942d531 | 137 | void |
c8806b6c NM |
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; | |
c8806b6c NM |
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); | |
c8806b6c NM |
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 */ |