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(vdev: snic->vdev); |
32 | snic->link_down_cnt = svnic_dev_link_down_cnt(vdev: 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, sg_cnt: 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(hdr: &req->hdr, typ: SNIC_REQ_EXCH_VER, status: 0, SCSI_NO_TAG, |
112 | hid: snic->config.hid, sg_cnt: 0, ctx: (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, os_buf: req, len: 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(hdr: &fwreq->hdr, typ: &typ, stat: &hdr_stat, cmnd_id: &cmnd_id, hid: &hid, ctx: &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(lock: &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(lock: &snic->snic_lock, flags); |
229 | |
230 | /* Additional delay to handle HW Resource initialization. */ |
231 | msleep(msecs: 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(x: &wait, timeout: msecs_to_jiffies(m: 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(lock: &snic->snic_lock, flags); |
254 | } while (ret && --nr_retries); |
255 | |
256 | return ret; |
257 | } /* end of snic_get_info */ |
258 | |