blob: f8ec6d63684644ab04388036f3f34d241cb9e701 [file] [log] [blame]
Bradley Grove26780d92013-08-23 10:35:45 -04001/*
2 * linux/drivers/scsi/esas2r/esas2r_vda.c
3 * esas2r driver VDA firmware interface functions
4 *
5 * Copyright (c) 2001-2013 ATTO Technology, Inc.
6 * (mailto:linuxdrivers@attotech.com)
7 */
8/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
9/*
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * NO WARRANTY
20 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
21 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
22 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
23 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
24 * solely responsible for determining the appropriateness of using and
25 * distributing the Program and assumes all risks associated with its
26 * exercise of rights under this Agreement, including but not limited to
27 * the risks and costs of program errors, damage to or loss of data,
28 * programs or equipment, and unavailability or interruption of operations.
29 *
30 * DISCLAIMER OF LIABILITY
31 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
32 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
35 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
36 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
37 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 */
43/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
44
45#include "esas2r.h"
46
47static u8 esas2r_vdaioctl_versions[] = {
48 ATTO_VDA_VER_UNSUPPORTED,
49 ATTO_VDA_FLASH_VER,
50 ATTO_VDA_VER_UNSUPPORTED,
51 ATTO_VDA_VER_UNSUPPORTED,
52 ATTO_VDA_CLI_VER,
53 ATTO_VDA_VER_UNSUPPORTED,
54 ATTO_VDA_CFG_VER,
55 ATTO_VDA_MGT_VER,
56 ATTO_VDA_GSV_VER
57};
58
59static void clear_vda_request(struct esas2r_request *rq);
60
61static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
62 struct esas2r_request *rq);
63
64/* Prepare a VDA IOCTL request to be sent to the firmware. */
65bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
66 struct atto_ioctl_vda *vi,
67 struct esas2r_request *rq,
68 struct esas2r_sg_context *sgc)
69{
70 u32 datalen = 0;
71 struct atto_vda_sge *firstsg = NULL;
72 u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
73
74 vi->status = ATTO_STS_SUCCESS;
75 vi->vda_status = RS_PENDING;
76
77 if (vi->function >= vercnt) {
78 vi->status = ATTO_STS_INV_FUNC;
79 return false;
80 }
81
82 if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
83 vi->status = ATTO_STS_INV_VERSION;
84 return false;
85 }
86
87 if (a->flags & AF_DEGRADED_MODE) {
88 vi->status = ATTO_STS_DEGRADED;
89 return false;
90 }
91
92 if (vi->function != VDA_FUNC_SCSI)
93 clear_vda_request(rq);
94
95 rq->vrq->scsi.function = vi->function;
96 rq->interrupt_cb = esas2r_complete_vda_ioctl;
97 rq->interrupt_cx = vi;
98
99 switch (vi->function) {
100 case VDA_FUNC_FLASH:
101
102 if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
103 && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
104 && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
105 vi->status = ATTO_STS_INV_FUNC;
106 return false;
107 }
108
109 if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
110 datalen = vi->data_length;
111
112 rq->vrq->flash.length = cpu_to_le32(datalen);
113 rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
114
115 memcpy(rq->vrq->flash.data.file.file_name,
116 vi->cmd.flash.data.file.file_name,
117 sizeof(vi->cmd.flash.data.file.file_name));
118
119 firstsg = rq->vrq->flash.data.file.sge;
120 break;
121
122 case VDA_FUNC_CLI:
123
124 datalen = vi->data_length;
125
126 rq->vrq->cli.cmd_rsp_len =
127 cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
128 rq->vrq->cli.length = cpu_to_le32(datalen);
129
130 firstsg = rq->vrq->cli.sge;
131 break;
132
133 case VDA_FUNC_MGT:
134 {
135 u8 *cmdcurr_offset = sgc->cur_offset
136 - offsetof(struct atto_ioctl_vda, data)
137 + offsetof(struct atto_ioctl_vda, cmd)
138 + offsetof(struct atto_ioctl_vda_mgt_cmd,
139 data);
140 /*
141 * build the data payload SGL here first since
142 * esas2r_sgc_init() will modify the S/G list offset for the
143 * management SGL (which is built below where the data SGL is
144 * usually built).
145 */
146
147 if (vi->data_length) {
148 u32 payldlen = 0;
149
150 if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
151 || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
152 rq->vrq->mgt.payld_sglst_offset =
153 (u8)offsetof(struct atto_vda_mgmt_req,
154 payld_sge);
155
156 payldlen = vi->data_length;
157 datalen = vi->cmd.mgt.data_length;
158 } else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
159 || vi->cmd.mgt.mgt_func ==
160 VDAMGT_DEV_INFO2_BYADDR) {
161 datalen = vi->data_length;
162 cmdcurr_offset = sgc->cur_offset;
163 } else {
164 vi->status = ATTO_STS_INV_PARAM;
165 return false;
166 }
167
168 /* Setup the length so building the payload SGL works */
169 rq->vrq->mgt.length = cpu_to_le32(datalen);
170
171 if (payldlen) {
172 rq->vrq->mgt.payld_length =
173 cpu_to_le32(payldlen);
174
175 esas2r_sgc_init(sgc, a, rq,
176 rq->vrq->mgt.payld_sge);
177 sgc->length = payldlen;
178
179 if (!esas2r_build_sg_list(a, rq, sgc)) {
180 vi->status = ATTO_STS_OUT_OF_RSRC;
181 return false;
182 }
183 }
184 } else {
185 datalen = vi->cmd.mgt.data_length;
186
187 rq->vrq->mgt.length = cpu_to_le32(datalen);
188 }
189
190 /*
191 * Now that the payload SGL is built, if any, setup to build
192 * the management SGL.
193 */
194 firstsg = rq->vrq->mgt.sge;
195 sgc->cur_offset = cmdcurr_offset;
196
197 /* Finish initializing the management request. */
198 rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
199 rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
200 rq->vrq->mgt.dev_index =
201 cpu_to_le32(vi->cmd.mgt.dev_index);
202
203 esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
204 break;
205 }
206
207 case VDA_FUNC_CFG:
208
209 if (vi->data_length
210 || vi->cmd.cfg.data_length == 0) {
211 vi->status = ATTO_STS_INV_PARAM;
212 return false;
213 }
214
215 if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
216 vi->status = ATTO_STS_INV_FUNC;
217 return false;
218 }
219
220 rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
221 rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
222
223 if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
224 memcpy(&rq->vrq->cfg.data,
225 &vi->cmd.cfg.data,
226 vi->cmd.cfg.data_length);
227
228 esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
229 &rq->vrq->cfg.data);
230 } else {
231 vi->status = ATTO_STS_INV_FUNC;
232
233 return false;
234 }
235
236 break;
237
238 case VDA_FUNC_GSV:
239
240 vi->cmd.gsv.rsp_len = vercnt;
241
242 memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
243 vercnt);
244
245 vi->vda_status = RS_SUCCESS;
246 break;
247
248 default:
249
250 vi->status = ATTO_STS_INV_FUNC;
251 return false;
252 }
253
254 if (datalen) {
255 esas2r_sgc_init(sgc, a, rq, firstsg);
256 sgc->length = datalen;
257
258 if (!esas2r_build_sg_list(a, rq, sgc)) {
259 vi->status = ATTO_STS_OUT_OF_RSRC;
260 return false;
261 }
262 }
263
264 esas2r_start_request(a, rq);
265
266 return true;
267}
268
269static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
270 struct esas2r_request *rq)
271{
272 struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
273
274 vi->vda_status = rq->req_stat;
275
276 switch (vi->function) {
277 case VDA_FUNC_FLASH:
278
279 if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
280 || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
281 vi->cmd.flash.data.file.file_size =
282 le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
283
284 break;
285
286 case VDA_FUNC_MGT:
287
288 vi->cmd.mgt.scan_generation =
289 rq->func_rsp.mgt_rsp.scan_generation;
290 vi->cmd.mgt.dev_index = le16_to_cpu(
291 rq->func_rsp.mgt_rsp.dev_index);
292
293 if (vi->data_length == 0)
294 vi->cmd.mgt.data_length =
295 le32_to_cpu(rq->func_rsp.mgt_rsp.length);
296
297 esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
298 break;
299
300 case VDA_FUNC_CFG:
301
302 if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
303 struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
304 struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
305
306 cfg->data_length =
307 cpu_to_le32(sizeof(struct atto_vda_cfg_init));
308 cfg->data.init.vda_version =
309 le32_to_cpu(rsp->vda_version);
310 cfg->data.init.fw_build = rsp->fw_build;
311
312 sprintf((char *)&cfg->data.init.fw_release,
313 "%1d.%02d",
314 (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
315 (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
316
317 if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
318 cfg->data.init.fw_version =
319 cfg->data.init.fw_build;
320 else
321 cfg->data.init.fw_version =
322 cfg->data.init.fw_release;
323 } else {
324 esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
325 &vi->cmd.cfg.data);
326 }
327
328 break;
329
330 case VDA_FUNC_CLI:
331
332 vi->cmd.cli.cmd_rsp_len =
333 le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
334 break;
335
336 default:
337
338 break;
339 }
340}
341
342/* Build a flash VDA request. */
343void esas2r_build_flash_req(struct esas2r_adapter *a,
344 struct esas2r_request *rq,
345 u8 sub_func,
346 u8 cksum,
347 u32 addr,
348 u32 length)
349{
350 struct atto_vda_flash_req *vrq = &rq->vrq->flash;
351
352 clear_vda_request(rq);
353
354 rq->vrq->scsi.function = VDA_FUNC_FLASH;
355
356 if (sub_func == VDA_FLASH_BEGINW
357 || sub_func == VDA_FLASH_WRITE
358 || sub_func == VDA_FLASH_READ)
359 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
360 data.sge);
361
362 vrq->length = cpu_to_le32(length);
363 vrq->flash_addr = cpu_to_le32(addr);
364 vrq->checksum = cksum;
365 vrq->sub_func = sub_func;
366}
367
368/* Build a VDA management request. */
369void esas2r_build_mgt_req(struct esas2r_adapter *a,
370 struct esas2r_request *rq,
371 u8 sub_func,
372 u8 scan_gen,
373 u16 dev_index,
374 u32 length,
375 void *data)
376{
377 struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
378
379 clear_vda_request(rq);
380
381 rq->vrq->scsi.function = VDA_FUNC_MGT;
382
383 vrq->mgt_func = sub_func;
384 vrq->scan_generation = scan_gen;
385 vrq->dev_index = cpu_to_le16(dev_index);
386 vrq->length = cpu_to_le32(length);
387
388 if (vrq->length) {
389 if (a->flags & AF_LEGACY_SGE_MODE) {
390 vrq->sg_list_offset = (u8)offsetof(
391 struct atto_vda_mgmt_req, sge);
392
393 vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
394 vrq->sge[0].address = cpu_to_le64(
395 rq->vrq_md->phys_addr +
396 sizeof(union atto_vda_req));
397 } else {
398 vrq->sg_list_offset = (u8)offsetof(
399 struct atto_vda_mgmt_req, prde);
400
401 vrq->prde[0].ctl_len = cpu_to_le32(length);
402 vrq->prde[0].address = cpu_to_le64(
403 rq->vrq_md->phys_addr +
404 sizeof(union atto_vda_req));
405 }
406 }
407
408 if (data) {
409 esas2r_nuxi_mgt_data(sub_func, data);
410
411 memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
412 length);
413 }
414}
415
416/* Build a VDA asyncronous event (AE) request. */
417void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
418{
419 struct atto_vda_ae_req *vrq = &rq->vrq->ae;
420
421 clear_vda_request(rq);
422
423 rq->vrq->scsi.function = VDA_FUNC_AE;
424
425 vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
426
427 if (a->flags & AF_LEGACY_SGE_MODE) {
428 vrq->sg_list_offset =
429 (u8)offsetof(struct atto_vda_ae_req, sge);
430 vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
431 vrq->sge[0].address = cpu_to_le64(
432 rq->vrq_md->phys_addr +
433 sizeof(union atto_vda_req));
434 } else {
435 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
436 prde);
437 vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
438 vrq->prde[0].address = cpu_to_le64(
439 rq->vrq_md->phys_addr +
440 sizeof(union atto_vda_req));
441 }
442}
443
444/* Build a VDA CLI request. */
445void esas2r_build_cli_req(struct esas2r_adapter *a,
446 struct esas2r_request *rq,
447 u32 length,
448 u32 cmd_rsp_len)
449{
450 struct atto_vda_cli_req *vrq = &rq->vrq->cli;
451
452 clear_vda_request(rq);
453
454 rq->vrq->scsi.function = VDA_FUNC_CLI;
455
456 vrq->length = cpu_to_le32(length);
457 vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
458 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
459}
460
461/* Build a VDA IOCTL request. */
462void esas2r_build_ioctl_req(struct esas2r_adapter *a,
463 struct esas2r_request *rq,
464 u32 length,
465 u8 sub_func)
466{
467 struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
468
469 clear_vda_request(rq);
470
471 rq->vrq->scsi.function = VDA_FUNC_IOCTL;
472
473 vrq->length = cpu_to_le32(length);
474 vrq->sub_func = sub_func;
475 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
476}
477
478/* Build a VDA configuration request. */
479void esas2r_build_cfg_req(struct esas2r_adapter *a,
480 struct esas2r_request *rq,
481 u8 sub_func,
482 u32 length,
483 void *data)
484{
485 struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
486
487 clear_vda_request(rq);
488
489 rq->vrq->scsi.function = VDA_FUNC_CFG;
490
491 vrq->sub_func = sub_func;
492 vrq->length = cpu_to_le32(length);
493
494 if (data) {
495 esas2r_nuxi_cfg_data(sub_func, data);
496
497 memcpy(&vrq->data, data, length);
498 }
499}
500
501static void clear_vda_request(struct esas2r_request *rq)
502{
503 u32 handle = rq->vrq->scsi.handle;
504
505 memset(rq->vrq, 0, sizeof(*rq->vrq));
506
507 rq->vrq->scsi.handle = handle;
508
509 rq->req_stat = RS_PENDING;
510
511 /* since the data buffer is separate clear that too */
512
513 memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
514
515 /*
516 * Setup next and prev pointer in case the request is not going through
517 * esas2r_start_request().
518 */
519
520 INIT_LIST_HEAD(&rq->req_list);
521}