xref: /openbmc/linux/drivers/scsi/esas2r/esas2r_vda.c (revision b34081f1)
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 (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 
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 
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. */
343 void 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. */
369 void 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. */
417 void 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. */
445 void 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. */
462 void 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. */
479 void 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 
501 static 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 }
522