525 lines · c
1/*2 * linux/drivers/scsi/esas2r/esas2r_vda.c3 * esas2r driver VDA firmware interface functions4 *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 modify11 * it under the terms of the GNU General Public License as published by12 * 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 of16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17 * GNU General Public License for more details.18 *19 * NO WARRANTY20 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR21 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT22 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,23 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is24 * solely responsible for determining the appropriateness of using and25 * distributing the Program and assumes all risks associated with its26 * exercise of rights under this Agreement, including but not limited to27 * 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 LIABILITY31 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY32 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL33 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR35 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE36 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED37 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES38 *39 * You should have received a copy of the GNU General Public License40 * along with this program; if not, write to the Free Software41 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA42 */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_VER57};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_FREAD103 && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE104 && 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_offset136 - 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 since142 * esas2r_sgc_init() will modify the S/G list offset for the143 * management SGL (which is built below where the data SGL is144 * usually built).145 */146 147 if (vi->data_length) {148 u32 payldlen = 0;149 150 if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ151 || 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_INFO2159 || 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 build192 * 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_length210 || 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_FINFO280 || 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 else324 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_BEGINW360 || sub_func == VDA_FLASH_WRITE361 || 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 through520 * esas2r_start_request().521 */522 523 INIT_LIST_HEAD(&rq->req_list);524}525