Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / scsi / esas2r / esas2r_vda.c
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 }