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 IOCTL request. */ 448 void esas2r_build_ioctl_req(struct esas2r_adapter *a, 449 struct esas2r_request *rq, 450 u32 length, 451 u8 sub_func) 452 { 453 struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl; 454 455 clear_vda_request(rq); 456 457 rq->vrq->scsi.function = VDA_FUNC_IOCTL; 458 459 vrq->length = cpu_to_le32(length); 460 vrq->sub_func = sub_func; 461 vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge); 462 } 463 464 /* Build a VDA configuration request. */ 465 void esas2r_build_cfg_req(struct esas2r_adapter *a, 466 struct esas2r_request *rq, 467 u8 sub_func, 468 u32 length, 469 void *data) 470 { 471 struct atto_vda_cfg_req *vrq = &rq->vrq->cfg; 472 473 clear_vda_request(rq); 474 475 rq->vrq->scsi.function = VDA_FUNC_CFG; 476 477 vrq->sub_func = sub_func; 478 vrq->length = cpu_to_le32(length); 479 480 if (data) { 481 esas2r_nuxi_cfg_data(sub_func, data); 482 483 memcpy(&vrq->data, data, length); 484 } 485 } 486 487 static void clear_vda_request(struct esas2r_request *rq) 488 { 489 u32 handle = rq->vrq->scsi.handle; 490 491 memset(rq->vrq, 0, sizeof(*rq->vrq)); 492 493 rq->vrq->scsi.handle = handle; 494 495 rq->req_stat = RS_PENDING; 496 497 /* since the data buffer is separate clear that too */ 498 499 memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN); 500 501 /* 502 * Setup next and prev pointer in case the request is not going through 503 * esas2r_start_request(). 504 */ 505 506 INIT_LIST_HEAD(&rq->req_list); 507 } 508