xref: /openbmc/linux/drivers/scsi/esas2r/esas2r_vda.c (revision 90f59ee4)
1 /*
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 
47 static 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 
59 static void clear_vda_request(struct esas2r_request *rq);
60 
61 static 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. */
65 bool 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 (test_bit(AF_DEGRADED_MODE, &a->flags)) {
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 
269 static 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 			char buf[sizeof(cfg->data.init.fw_release) + 1];
306 
307 			cfg->data_length =
308 				cpu_to_le32(sizeof(struct atto_vda_cfg_init));
309 			cfg->data.init.vda_version =
310 				le32_to_cpu(rsp->vda_version);
311 			cfg->data.init.fw_build = rsp->fw_build;
312 
313 			snprintf(buf, sizeof(buf), "%1.1u.%2.2u",
314 				 (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
315 				 (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
316 
317 			memcpy(&cfg->data.init.fw_release, buf,
318 			       sizeof(cfg->data.init.fw_release));
319 
320 			if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
321 				cfg->data.init.fw_version =
322 					cfg->data.init.fw_build;
323 			else
324 				cfg->data.init.fw_version =
325 					cfg->data.init.fw_release;
326 		} else {
327 			esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
328 					     &vi->cmd.cfg.data);
329 		}
330 
331 		break;
332 
333 	case VDA_FUNC_CLI:
334 
335 		vi->cmd.cli.cmd_rsp_len =
336 			le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
337 		break;
338 
339 	default:
340 
341 		break;
342 	}
343 }
344 
345 /* Build a flash VDA request. */
346 void esas2r_build_flash_req(struct esas2r_adapter *a,
347 			    struct esas2r_request *rq,
348 			    u8 sub_func,
349 			    u8 cksum,
350 			    u32 addr,
351 			    u32 length)
352 {
353 	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
354 
355 	clear_vda_request(rq);
356 
357 	rq->vrq->scsi.function = VDA_FUNC_FLASH;
358 
359 	if (sub_func == VDA_FLASH_BEGINW
360 	    || sub_func == VDA_FLASH_WRITE
361 	    || sub_func == VDA_FLASH_READ)
362 		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
363 						   data.sge);
364 
365 	vrq->length = cpu_to_le32(length);
366 	vrq->flash_addr = cpu_to_le32(addr);
367 	vrq->checksum = cksum;
368 	vrq->sub_func = sub_func;
369 }
370 
371 /* Build a VDA management request. */
372 void esas2r_build_mgt_req(struct esas2r_adapter *a,
373 			  struct esas2r_request *rq,
374 			  u8 sub_func,
375 			  u8 scan_gen,
376 			  u16 dev_index,
377 			  u32 length,
378 			  void *data)
379 {
380 	struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
381 
382 	clear_vda_request(rq);
383 
384 	rq->vrq->scsi.function = VDA_FUNC_MGT;
385 
386 	vrq->mgt_func = sub_func;
387 	vrq->scan_generation = scan_gen;
388 	vrq->dev_index = cpu_to_le16(dev_index);
389 	vrq->length = cpu_to_le32(length);
390 
391 	if (vrq->length) {
392 		if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
393 			vrq->sg_list_offset = (u8)offsetof(
394 				struct atto_vda_mgmt_req, sge);
395 
396 			vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
397 			vrq->sge[0].address = cpu_to_le64(
398 				rq->vrq_md->phys_addr +
399 				sizeof(union atto_vda_req));
400 		} else {
401 			vrq->sg_list_offset = (u8)offsetof(
402 				struct atto_vda_mgmt_req, prde);
403 
404 			vrq->prde[0].ctl_len = cpu_to_le32(length);
405 			vrq->prde[0].address = cpu_to_le64(
406 				rq->vrq_md->phys_addr +
407 				sizeof(union atto_vda_req));
408 		}
409 	}
410 
411 	if (data) {
412 		esas2r_nuxi_mgt_data(sub_func, data);
413 
414 		memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
415 		       length);
416 	}
417 }
418 
419 /* Build a VDA asyncronous event (AE) request. */
420 void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
421 {
422 	struct atto_vda_ae_req *vrq = &rq->vrq->ae;
423 
424 	clear_vda_request(rq);
425 
426 	rq->vrq->scsi.function = VDA_FUNC_AE;
427 
428 	vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
429 
430 	if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
431 		vrq->sg_list_offset =
432 			(u8)offsetof(struct atto_vda_ae_req, sge);
433 		vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
434 		vrq->sge[0].address = cpu_to_le64(
435 			rq->vrq_md->phys_addr +
436 			sizeof(union atto_vda_req));
437 	} else {
438 		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
439 						   prde);
440 		vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
441 		vrq->prde[0].address = cpu_to_le64(
442 			rq->vrq_md->phys_addr +
443 			sizeof(union atto_vda_req));
444 	}
445 }
446 
447 /* Build a VDA CLI request. */
448 void esas2r_build_cli_req(struct esas2r_adapter *a,
449 			  struct esas2r_request *rq,
450 			  u32 length,
451 			  u32 cmd_rsp_len)
452 {
453 	struct atto_vda_cli_req *vrq = &rq->vrq->cli;
454 
455 	clear_vda_request(rq);
456 
457 	rq->vrq->scsi.function = VDA_FUNC_CLI;
458 
459 	vrq->length = cpu_to_le32(length);
460 	vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
461 	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
462 }
463 
464 /* Build a VDA IOCTL request. */
465 void esas2r_build_ioctl_req(struct esas2r_adapter *a,
466 			    struct esas2r_request *rq,
467 			    u32 length,
468 			    u8 sub_func)
469 {
470 	struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
471 
472 	clear_vda_request(rq);
473 
474 	rq->vrq->scsi.function = VDA_FUNC_IOCTL;
475 
476 	vrq->length = cpu_to_le32(length);
477 	vrq->sub_func = sub_func;
478 	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
479 }
480 
481 /* Build a VDA configuration request. */
482 void esas2r_build_cfg_req(struct esas2r_adapter *a,
483 			  struct esas2r_request *rq,
484 			  u8 sub_func,
485 			  u32 length,
486 			  void *data)
487 {
488 	struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
489 
490 	clear_vda_request(rq);
491 
492 	rq->vrq->scsi.function = VDA_FUNC_CFG;
493 
494 	vrq->sub_func = sub_func;
495 	vrq->length = cpu_to_le32(length);
496 
497 	if (data) {
498 		esas2r_nuxi_cfg_data(sub_func, data);
499 
500 		memcpy(&vrq->data, data, length);
501 	}
502 }
503 
504 static void clear_vda_request(struct esas2r_request *rq)
505 {
506 	u32 handle = rq->vrq->scsi.handle;
507 
508 	memset(rq->vrq, 0, sizeof(*rq->vrq));
509 
510 	rq->vrq->scsi.handle = handle;
511 
512 	rq->req_stat = RS_PENDING;
513 
514 	/* since the data buffer is separate clear that too */
515 
516 	memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
517 
518 	/*
519 	 * Setup next and prev pointer in case the request is not going through
520 	 * esas2r_start_request().
521 	 */
522 
523 	INIT_LIST_HEAD(&rq->req_list);
524 }
525