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