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
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 (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
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			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. */
346void 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. */
372void 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. */
420void 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. */
448void 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. */
465void 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. */
482void 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
504static 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